]> git.cameronkatri.com Git - mandoc.git/commitdiff
Remove the dependency on SQLite without loss of functionality.
authorIngo Schwarze <schwarze@openbsd.org>
Tue, 19 Jul 2016 21:31:55 +0000 (21:31 +0000)
committerIngo Schwarze <schwarze@openbsd.org>
Tue, 19 Jul 2016 21:31:55 +0000 (21:31 +0000)
Stop supporting systems that don't have mmap(3).
Drop the obsolete names_check() now that we deleted MLINKS.

26 files changed:
INSTALL
Makefile
Makefile.depend
TODO
compat_sqlite3_errstr.c [deleted file]
configure
configure.local.example
dba.c [new file with mode: 0644]
dba.h [new file with mode: 0644]
dba_array.c [new file with mode: 0644]
dba_array.h [new file with mode: 0644]
dba_read.c [new file with mode: 0644]
dba_write.c [new file with mode: 0644]
dba_write.h [new file with mode: 0644]
dbm.c [new file with mode: 0644]
dbm.h [new file with mode: 0644]
dbm_map.c [new file with mode: 0644]
dbm_map.h [moved from mansearch_const.c with 57% similarity]
main.c
mandocdb.c
mansearch.c
mansearch.h
read.c
test-mmap.c [deleted file]
test-sqlite3.c [deleted file]
test-sqlite3_errstr.c [deleted file]

diff --git a/INSTALL b/INSTALL
index 58bef6a7358c0de8785cfa534df7624095fd29bd..765bc5680dc7a37bdc49dfa05cf58219b5e3bae3 100644 (file)
--- a/INSTALL
+++ b/INSTALL
@@ -1,4 +1,4 @@
-$Id: INSTALL,v 1.15 2016/07/14 11:09:06 schwarze Exp $
+$Id: INSTALL,v 1.16 2016/07/19 21:31:55 schwarze Exp $
 
 About mdocml, the portable mandoc distribution
 ----------------------------------------------
 
 About mdocml, the portable mandoc distribution
 ----------------------------------------------
@@ -66,7 +66,7 @@ Otherwise, if your system uses man.conf(5), make sure it contains
 a "manpath" line for each directory tree, and the order of these
 lines meets your wishes.
 
 a "manpath" line for each directory tree, and the order of these
 lines meets your wishes.
 
-7. If you compiled with database support, run the command "sudo
+7. Run the command "sudo
 makewhatis" to build mandoc.db(5) databases in all the directory
 trees configured in step 6.  Whenever installing new manual pages,
 re-run makewhatis(8) to update the databases, or apropos(1) will
 makewhatis" to build mandoc.db(5) databases in all the directory
 trees configured in step 6.  Whenever installing new manual pages,
 re-run makewhatis(8) to update the databases, or apropos(1) will
@@ -84,20 +84,9 @@ manual page source.
 
 Understanding mandoc dependencies
 ---------------------------------
 
 Understanding mandoc dependencies
 ---------------------------------
-The mandoc(1), man(1), and demandoc(1) utilities only depend
-on the zlib library for decompressing gzipped manual pages,
-but makewhatis(8) and apropos(1) depend on the following
-additional software:
-
-1. The SQLite database system, see <http://sqlite.org/>.
-The recommended version of SQLite is 3.8.4.3 or newer.  The mandoc
-toolset is known to work with version 3.7.5 or newer.  Versions
-older than 3.8.3 may not achieve full performance due to the
-missing SQLITE_DETERMINISTIC optimization flag.  Versions older
-than 3.8.0 may not show full error information if opening a database
-fails due to the missing sqlite3_errstr() API.  Both are very minor
-problems, apropos(1) is fully usable with SQLite 3.7.5.  Versions
-older than 3.7.5 may or may not work, they have not been tested.
+The following libraries are required:
+
+1. zlib for decompressing gzipped manual pages.
 
 2. The fts(3) directory traversion functions.
 If your system does not have them, the bundled compatibility version
 
 2. The fts(3) directory traversion functions.
 If your system does not have them, the bundled compatibility version
index f76b13763136dcef9281031ffd2c5ecad9816070..df1116afad5ee310c7462dacd8a5f1ee9dbcb9c3 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,4 +1,4 @@
-# $Id: Makefile,v 1.488 2016/07/12 05:18:38 kristaps Exp $
+# $Id: Makefile,v 1.489 2016/07/19 21:31:55 schwarze Exp $
 #
 # Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
 # Copyright (c) 2011, 2013-2016 Ingo Schwarze <schwarze@openbsd.org>
 #
 # Copyright (c) 2010, 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
 # Copyright (c) 2011, 2013-2016 Ingo Schwarze <schwarze@openbsd.org>
@@ -15,7 +15,7 @@
 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 # ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 # OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
-VERSION = 1.13.4
+VERSION = 1.14.0
 
 # === LIST OF FILES ====================================================
 
 
 # === LIST OF FILES ====================================================
 
@@ -26,7 +26,6 @@ TESTSRCS       = test-dirent-namlen.c \
                   test-getsubopt.c \
                   test-isblank.c \
                   test-mkdtemp.c \
                   test-getsubopt.c \
                   test-isblank.c \
                   test-mkdtemp.c \
-                  test-mmap.c \
                   test-ohash.c \
                   test-pledge.c \
                   test-progname.c \
                   test-ohash.c \
                   test-pledge.c \
                   test-progname.c \
@@ -34,8 +33,6 @@ TESTSRCS       = test-dirent-namlen.c \
                   test-rewb-bsd.c \
                   test-rewb-sysv.c \
                   test-sandbox_init.c \
                   test-rewb-bsd.c \
                   test-rewb-sysv.c \
                   test-sandbox_init.c \
-                  test-sqlite3.c \
-                  test-sqlite3_errstr.c \
                   test-strcasestr.c \
                   test-stringlist.c \
                   test-strlcat.c \
                   test-strcasestr.c \
                   test-stringlist.c \
                   test-strlcat.c \
@@ -58,7 +55,6 @@ SRCS           = att.c \
                   compat_ohash.c \
                   compat_progname.c \
                   compat_reallocarray.c \
                   compat_ohash.c \
                   compat_progname.c \
                   compat_reallocarray.c \
-                  compat_sqlite3_errstr.c \
                   compat_strcasestr.c \
                   compat_stringlist.c \
                   compat_strlcat.c \
                   compat_strcasestr.c \
                   compat_stringlist.c \
                   compat_strlcat.c \
@@ -66,6 +62,12 @@ SRCS          = att.c \
                   compat_strsep.c \
                   compat_strtonum.c \
                   compat_vasprintf.c \
                   compat_strsep.c \
                   compat_strtonum.c \
                   compat_vasprintf.c \
+                  dba.c \
+                  dba_array.c \
+                  dba_read.c \
+                  dba_write.c \
+                  dbm.c \
+                  dbm_map.c \
                   demandoc.c \
                   eqn.c \
                   eqn_html.c \
                   demandoc.c \
                   eqn.c \
                   eqn_html.c \
@@ -86,7 +88,6 @@ SRCS           = att.c \
                   manpage.c \
                   manpath.c \
                   mansearch.c \
                   manpage.c \
                   manpath.c \
                   mansearch.c \
-                  mansearch_const.c \
                   mdoc.c \
                   mdoc_argv.c \
                   mdoc_hash.c \
                   mdoc.c \
                   mdoc_argv.c \
                   mdoc_hash.c \
@@ -128,6 +129,11 @@ DISTFILES   = INSTALL \
                   compat_stringlist.h \
                   configure \
                   configure.local.example \
                   compat_stringlist.h \
                   configure \
                   configure.local.example \
+                  dba.h \
+                  dba_array.h \
+                  dba_write.h \
+                  dbm.h \
+                  dbm_map.h \
                   demandoc.1 \
                   eqn.7 \
                   gmdiff \
                   demandoc.1 \
                   eqn.7 \
                   gmdiff \
@@ -220,7 +226,6 @@ COMPAT_OBJS  = compat_err.o \
                   compat_ohash.o \
                   compat_progname.o \
                   compat_reallocarray.o \
                   compat_ohash.o \
                   compat_progname.o \
                   compat_reallocarray.o \
-                  compat_sqlite3_errstr.o \
                   compat_strcasestr.o \
                   compat_strlcat.o \
                   compat_strlcpy.o \
                   compat_strcasestr.o \
                   compat_strlcat.o \
                   compat_strlcpy.o \
@@ -244,28 +249,35 @@ MANDOC_TERM_OBJS = eqn_term.o \
                   term_ps.o \
                   tbl_term.o
 
                   term_ps.o \
                   tbl_term.o
 
-BASE_OBJS       = $(MANDOC_HTML_OBJS) \
+DBM_OBJS        = dbm.o \
+                  dbm_map.o \
+                  mansearch.o
+
+DBA_OBJS        = dba.o \
+                  dba_array.o \
+                  dba_read.o \
+                  dba_write.o \
+                  mandocdb.o
+
+MAIN_OBJS       = $(MANDOC_HTML_OBJS) \
                   $(MANDOC_MAN_OBJS) \
                   $(MANDOC_TERM_OBJS) \
                   $(MANDOC_MAN_OBJS) \
                   $(MANDOC_TERM_OBJS) \
+                  $(DBM_OBJS) \
+                  $(DBA_OBJS) \
                   main.o \
                   manpath.o \
                   out.o \
                   tag.o \
                   tree.o
 
                   main.o \
                   manpath.o \
                   out.o \
                   tag.o \
                   tree.o
 
-MAIN_OBJS       = $(BASE_OBJS)
-
-DB_OBJS                 = mandocdb.o \
-                  mansearch.o \
-                  mansearch_const.o
-
 CGI_OBJS        = $(MANDOC_HTML_OBJS) \
 CGI_OBJS        = $(MANDOC_HTML_OBJS) \
+                  $(DBM_OBJS) \
                   cgi.o \
                   cgi.o \
-                  mansearch.o \
-                  mansearch_const.o \
                   out.o
 
                   out.o
 
-MANPAGE_OBJS    = manpage.o mansearch.o mansearch_const.o manpath.o
+MANPAGE_OBJS    = $(DBM_OBJS) \
+                  manpage.o \
+                  manpath.o
 
 DEMANDOC_OBJS   = demandoc.o
 
 
 DEMANDOC_OBJS   = demandoc.o
 
@@ -329,7 +341,7 @@ www: $(WWW_OBJS) $(WWW_MANS)
 
 $(WWW_MANS): mandoc
 
 
 $(WWW_MANS): mandoc
 
-.PHONY: base-install cgi-install db-install install www-install
+.PHONY: base-install cgi-install install www-install
 .PHONY: clean distclean depend
 
 include Makefile.depend
 .PHONY: clean distclean depend
 
 include Makefile.depend
@@ -341,7 +353,7 @@ distclean: clean
 
 clean:
        rm -f libmandoc.a $(LIBMANDOC_OBJS) $(COMPAT_OBJS)
 
 clean:
        rm -f libmandoc.a $(LIBMANDOC_OBJS) $(COMPAT_OBJS)
-       rm -f mandoc $(BASE_OBJS) $(DB_OBJS)
+       rm -f mandoc $(MAIN_OBJS)
        rm -f man.cgi $(CGI_OBJS)
        rm -f manpage $(MANPAGE_OBJS)
        rm -f demandoc $(DEMANDOC_OBJS)
        rm -f man.cgi $(CGI_OBJS)
        rm -f manpage $(MANPAGE_OBJS)
        rm -f demandoc $(DEMANDOC_OBJS)
@@ -351,47 +363,40 @@ clean:
 
 base-install: base-build
        mkdir -p $(DESTDIR)$(BINDIR)
 
 base-install: base-build
        mkdir -p $(DESTDIR)$(BINDIR)
+       mkdir -p $(DESTDIR)$(SBINDIR)
        mkdir -p $(DESTDIR)$(LIBDIR)
        mkdir -p $(DESTDIR)$(INCLUDEDIR)
        mkdir -p $(DESTDIR)$(MANDIR)/man1
        mkdir -p $(DESTDIR)$(MANDIR)/man3
        mkdir -p $(DESTDIR)$(MANDIR)/man5
        mkdir -p $(DESTDIR)$(MANDIR)/man7
        mkdir -p $(DESTDIR)$(LIBDIR)
        mkdir -p $(DESTDIR)$(INCLUDEDIR)
        mkdir -p $(DESTDIR)$(MANDIR)/man1
        mkdir -p $(DESTDIR)$(MANDIR)/man3
        mkdir -p $(DESTDIR)$(MANDIR)/man5
        mkdir -p $(DESTDIR)$(MANDIR)/man7
+       mkdir -p $(DESTDIR)$(MANDIR)/man8
        $(INSTALL_PROGRAM) mandoc demandoc $(DESTDIR)$(BINDIR)
        $(INSTALL_PROGRAM) soelim $(DESTDIR)$(BINDIR)/$(BINM_SOELIM)
        ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/$(BINM_MAN)
        $(INSTALL_PROGRAM) mandoc demandoc $(DESTDIR)$(BINDIR)
        $(INSTALL_PROGRAM) soelim $(DESTDIR)$(BINDIR)/$(BINM_SOELIM)
        ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/$(BINM_MAN)
+       ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/$(BINM_APROPOS)
+       ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/$(BINM_WHATIS)
+       ln -f $(DESTDIR)$(BINDIR)/mandoc \
+               $(DESTDIR)$(SBINDIR)/$(BINM_MAKEWHATIS)
        $(INSTALL_LIB) libmandoc.a $(DESTDIR)$(LIBDIR)
        $(INSTALL_LIB) man.h mandoc.h mandoc_aux.h mdoc.h roff.h \
                $(DESTDIR)$(INCLUDEDIR)
        $(INSTALL_MAN) mandoc.1 demandoc.1 $(DESTDIR)$(MANDIR)/man1
        $(INSTALL_MAN) soelim.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_SOELIM).1
        $(INSTALL_MAN) man.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_MAN).1
        $(INSTALL_LIB) libmandoc.a $(DESTDIR)$(LIBDIR)
        $(INSTALL_LIB) man.h mandoc.h mandoc_aux.h mdoc.h roff.h \
                $(DESTDIR)$(INCLUDEDIR)
        $(INSTALL_MAN) mandoc.1 demandoc.1 $(DESTDIR)$(MANDIR)/man1
        $(INSTALL_MAN) soelim.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_SOELIM).1
        $(INSTALL_MAN) man.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_MAN).1
+       $(INSTALL_MAN) apropos.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_APROPOS).1
+       ln -f $(DESTDIR)$(MANDIR)/man1/$(BINM_APROPOS).1 \
+               $(DESTDIR)$(MANDIR)/man1/$(BINM_WHATIS).1
        $(INSTALL_MAN) mandoc.3 mandoc_escape.3 mandoc_malloc.3 \
        $(INSTALL_MAN) mandoc.3 mandoc_escape.3 mandoc_malloc.3 \
-               mchars_alloc.3 tbl.3 $(DESTDIR)$(MANDIR)/man3
+               mansearch.3 mchars_alloc.3 tbl.3 $(DESTDIR)$(MANDIR)/man3
        $(INSTALL_MAN) man.conf.5 $(DESTDIR)$(MANDIR)/man5/${MANM_MANCONF}.5
        $(INSTALL_MAN) man.conf.5 $(DESTDIR)$(MANDIR)/man5/${MANM_MANCONF}.5
+       $(INSTALL_MAN) mandoc.db.5 $(DESTDIR)$(MANDIR)/man5
        $(INSTALL_MAN) man.7 $(DESTDIR)$(MANDIR)/man7/${MANM_MAN}.7
        $(INSTALL_MAN) mdoc.7 $(DESTDIR)$(MANDIR)/man7/${MANM_MDOC}.7
        $(INSTALL_MAN) roff.7 $(DESTDIR)$(MANDIR)/man7/${MANM_ROFF}.7
        $(INSTALL_MAN) eqn.7 $(DESTDIR)$(MANDIR)/man7/${MANM_EQN}.7
        $(INSTALL_MAN) tbl.7 $(DESTDIR)$(MANDIR)/man7/${MANM_TBL}.7
        $(INSTALL_MAN) mandoc_char.7 $(DESTDIR)$(MANDIR)/man7
        $(INSTALL_MAN) man.7 $(DESTDIR)$(MANDIR)/man7/${MANM_MAN}.7
        $(INSTALL_MAN) mdoc.7 $(DESTDIR)$(MANDIR)/man7/${MANM_MDOC}.7
        $(INSTALL_MAN) roff.7 $(DESTDIR)$(MANDIR)/man7/${MANM_ROFF}.7
        $(INSTALL_MAN) eqn.7 $(DESTDIR)$(MANDIR)/man7/${MANM_EQN}.7
        $(INSTALL_MAN) tbl.7 $(DESTDIR)$(MANDIR)/man7/${MANM_TBL}.7
        $(INSTALL_MAN) mandoc_char.7 $(DESTDIR)$(MANDIR)/man7
-
-db-install: base-build
-       mkdir -p $(DESTDIR)$(BINDIR)
-       mkdir -p $(DESTDIR)$(SBINDIR)
-       mkdir -p $(DESTDIR)$(MANDIR)/man1
-       mkdir -p $(DESTDIR)$(MANDIR)/man3
-       mkdir -p $(DESTDIR)$(MANDIR)/man5
-       mkdir -p $(DESTDIR)$(MANDIR)/man8
-       ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/$(BINM_APROPOS)
-       ln -f $(DESTDIR)$(BINDIR)/mandoc $(DESTDIR)$(BINDIR)/$(BINM_WHATIS)
-       ln -f $(DESTDIR)$(BINDIR)/mandoc \
-               $(DESTDIR)$(SBINDIR)/$(BINM_MAKEWHATIS)
-       $(INSTALL_MAN) apropos.1 $(DESTDIR)$(MANDIR)/man1/$(BINM_APROPOS).1
-       ln -f $(DESTDIR)$(MANDIR)/man1/$(BINM_APROPOS).1 \
-               $(DESTDIR)$(MANDIR)/man1/$(BINM_WHATIS).1
-       $(INSTALL_MAN) mansearch.3 $(DESTDIR)$(MANDIR)/man3
-       $(INSTALL_MAN) mandoc.db.5 $(DESTDIR)$(MANDIR)/man5
        $(INSTALL_MAN) makewhatis.8 \
                $(DESTDIR)$(MANDIR)/man8/$(BINM_MAKEWHATIS).8
 
        $(INSTALL_MAN) makewhatis.8 \
                $(DESTDIR)$(MANDIR)/man8/$(BINM_MAKEWHATIS).8
 
index d2d2e001ffa3e69aa71005d764cc2e3540ae1424..e0c3a2e97ac0d683d765850304f108f22a8df7d7 100644 (file)
@@ -1,16 +1,15 @@
 att.o: att.c config.h roff.h mdoc.h libmdoc.h
 cgi.o: cgi.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h main.h manconf.h mansearch.h cgi.h
 att.o: att.c config.h roff.h mdoc.h libmdoc.h
 cgi.o: cgi.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h main.h manconf.h mansearch.h cgi.h
-chars.o: chars.c config.h mandoc.h mandoc_aux.h mandoc_ohash.h compat_ohash.h libmandoc.h
+chars.o: chars.c config.h mandoc.h mandoc_aux.h mandoc_ohash.h libmandoc.h
 compat_err.o: compat_err.c config.h
 compat_err.o: compat_err.c config.h
-compat_fts.o: compat_fts.c config.h compat_fts.h
+compat_fts.o: compat_fts.c config.h
 compat_getline.o: compat_getline.c config.h
 compat_getsubopt.o: compat_getsubopt.c config.h
 compat_isblank.o: compat_isblank.c config.h
 compat_mkdtemp.o: compat_mkdtemp.c config.h
 compat_getline.o: compat_getline.c config.h
 compat_getsubopt.o: compat_getsubopt.c config.h
 compat_isblank.o: compat_isblank.c config.h
 compat_mkdtemp.o: compat_mkdtemp.c config.h
-compat_ohash.o: compat_ohash.c config.h compat_ohash.h
+compat_ohash.o: compat_ohash.c config.h
 compat_progname.o: compat_progname.c config.h
 compat_reallocarray.o: compat_reallocarray.c config.h
 compat_progname.o: compat_progname.c config.h
 compat_reallocarray.o: compat_reallocarray.c config.h
-compat_sqlite3_errstr.o: compat_sqlite3_errstr.c config.h
 compat_strcasestr.o: compat_strcasestr.c config.h
 compat_stringlist.o: compat_stringlist.c config.h compat_stringlist.h
 compat_strlcat.o: compat_strlcat.c config.h
 compat_strcasestr.o: compat_strcasestr.c config.h
 compat_stringlist.o: compat_stringlist.c config.h compat_stringlist.h
 compat_strlcat.o: compat_strlcat.c config.h
@@ -18,6 +17,12 @@ compat_strlcpy.o: compat_strlcpy.c config.h
 compat_strsep.o: compat_strsep.c config.h
 compat_strtonum.o: compat_strtonum.c config.h
 compat_vasprintf.o: compat_vasprintf.c config.h
 compat_strsep.o: compat_strsep.c config.h
 compat_strtonum.o: compat_strtonum.c config.h
 compat_vasprintf.o: compat_vasprintf.c config.h
+dba.o: dba.c mandoc_aux.h mansearch.h dba_write.h dba_array.h dba.h
+dba_array.o: dba_array.c mandoc_aux.h dba_write.h dba_array.h
+dba_read.o: dba_read.c mandoc_aux.h mansearch.h dba_array.h dba.h dbm.h
+dba_write.o: dba_write.c dba_write.h
+dbm.o: dbm.c mansearch.h dbm_map.h dbm.h
+dbm_map.o: dbm_map.c mansearch.h dbm_map.h dbm.h
 demandoc.o: demandoc.c config.h roff.h man.h mdoc.h mandoc.h
 eqn.o: eqn.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
 eqn_html.o: eqn_html.c config.h mandoc.h out.h html.h
 demandoc.o: demandoc.c config.h roff.h man.h mdoc.h mandoc.h
 eqn.o: eqn.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
 eqn_html.o: eqn_html.c config.h mandoc.h out.h html.h
@@ -34,11 +39,10 @@ man_validate.o: man_validate.c config.h mandoc_aux.h mandoc.h roff.h man.h libma
 mandoc.o: mandoc.c config.h mandoc.h mandoc_aux.h libmandoc.h
 mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h
 mandoc_ohash.o: mandoc_ohash.c mandoc_aux.h mandoc_ohash.h compat_ohash.h
 mandoc.o: mandoc.c config.h mandoc.h mandoc_aux.h libmandoc.h
 mandoc_aux.o: mandoc_aux.c config.h mandoc.h mandoc_aux.h
 mandoc_ohash.o: mandoc_ohash.c mandoc_aux.h mandoc_ohash.h compat_ohash.h
-mandocdb.o: mandocdb.c config.h compat_fts.h mandoc_aux.h mandoc_ohash.h compat_ohash.h mandoc.h roff.h mdoc.h man.h manconf.h mansearch.h
+mandocdb.o: mandocdb.c config.h mandoc_aux.h mandoc_ohash.h mandoc.h roff.h mdoc.h man.h manconf.h mansearch.h dba_array.h dba.h
 manpage.o: manpage.c config.h manconf.h mansearch.h
 manpath.o: manpath.c config.h mandoc_aux.h manconf.h
 manpage.o: manpage.c config.h manconf.h mansearch.h
 manpath.o: manpath.c config.h mandoc_aux.h manconf.h
-mansearch.o: mansearch.c config.h mandoc.h mandoc_aux.h mandoc_ohash.h compat_ohash.h manconf.h mansearch.h
-mansearch_const.o: mansearch_const.c config.h mansearch.h
+mansearch.o: mansearch.c mandoc.h mandoc_aux.h mandoc_ohash.h compat_ohash.h manconf.h mansearch.h dbm.h
 mdoc.o: mdoc.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
 mdoc_argv.o: mdoc_argv.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
 mdoc_hash.o: mdoc_hash.c config.h mandoc.h roff.h mdoc.h libmandoc.h libmdoc.h
 mdoc.o: mdoc.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
 mdoc_argv.o: mdoc_argv.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h libmandoc.h roff_int.h libmdoc.h
 mdoc_hash.o: mdoc_hash.c config.h mandoc.h roff.h mdoc.h libmandoc.h libmdoc.h
@@ -55,7 +59,7 @@ read.o: read.c config.h mandoc_aux.h mandoc.h roff.h mdoc.h man.h libmandoc.h ro
 roff.o: roff.c config.h mandoc.h mandoc_aux.h roff.h libmandoc.h roff_int.h libroff.h predefs.in
 soelim.o: soelim.c config.h compat_stringlist.h
 st.o: st.c config.h roff.h mdoc.h libmdoc.h st.in
 roff.o: roff.c config.h mandoc.h mandoc_aux.h roff.h libmandoc.h roff_int.h libroff.h predefs.in
 soelim.o: soelim.c config.h compat_stringlist.h
 st.o: st.c config.h roff.h mdoc.h libmdoc.h st.in
-tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h compat_ohash.h tag.h
+tag.o: tag.c config.h mandoc_aux.h mandoc_ohash.h tag.h
 tbl.o: tbl.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
 tbl_data.o: tbl_data.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
 tbl_html.o: tbl_html.c config.h mandoc.h out.h html.h
 tbl.o: tbl.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
 tbl_data.o: tbl_data.c config.h mandoc.h mandoc_aux.h libmandoc.h libroff.h
 tbl_html.o: tbl_html.c config.h mandoc.h out.h html.h
diff --git a/TODO b/TODO
index 99a16a9e62a4575eff1f6e42d69950491aa1d58a..c22d1978b039749b62a152ddefc32cbdc1f92fa4 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,6 +1,6 @@
 ************************************************************************
 * Official mandoc TODO.
 ************************************************************************
 * Official mandoc TODO.
-* $Id: TODO,v 1.218 2016/06/05 21:06:04 schwarze Exp $
+* $Id: TODO,v 1.219 2016/07/19 21:31:55 schwarze Exp $
 ************************************************************************
 
 Many issues are annotated for difficulty as follows:
 ************************************************************************
 
 Many issues are annotated for difficulty as follows:
@@ -612,7 +612,6 @@ are mere guesses, and some may be wrong.
 ************************************************************************
 
 - Why are we using MAP_SHARED, not MAP_PRIVATE for mmap(2)?
 ************************************************************************
 
 - Why are we using MAP_SHARED, not MAP_PRIVATE for mmap(2)?
-  How does SQLITE_CONFIG_PAGECACHE actually work?  Document it!
   from kristaps@  Sat, 09 Aug 2014 13:51:36 +0200
 
 Several areas can be cleaned up to make mandoc even faster.  These are
   from kristaps@  Sat, 09 Aug 2014 13:51:36 +0200
 
 Several areas can be cleaned up to make mandoc even faster.  These are
@@ -646,11 +645,6 @@ Several areas can be cleaned up to make mandoc even faster.  These are
 - struct mparse refactoring
   Steffen Nurpmeso  Thu, 04 Sep 2014 12:50:00 +0200
 
 - struct mparse refactoring
   Steffen Nurpmeso  Thu, 04 Sep 2014 12:50:00 +0200
 
-- Consider creating some views that will make the database more
-  readable from the sqlite3 shell.  Consider using them to
-  abstract from the database structure, too.
-  suggested by espie@  Sat, 19 Apr 2014 14:52:57 +0200
-
 ************************************************************************
 * CGI issues
 ************************************************************************
 ************************************************************************
 * CGI issues
 ************************************************************************
diff --git a/compat_sqlite3_errstr.c b/compat_sqlite3_errstr.c
deleted file mode 100644 (file)
index 8a6ace2..0000000
+++ /dev/null
@@ -1,16 +0,0 @@
-#include "config.h"
-
-#if HAVE_SQLITE3_ERRSTR
-
-int dummy;
-
-#else
-
-const char *
-sqlite3_errstr(int rc)
-{
-
-       return rc ? "unknown error" : "not an error";
-}
-
-#endif
index 9e7128e616cb99922d80a977b1e63b34b3a7499d..77993f7dd75a20c958591eda464b4af3ae9d0e34 100755 (executable)
--- a/configure
+++ b/configure
@@ -40,10 +40,8 @@ CFLAGS="${CFLAGS} -Wno-unused-parameter"
 LDADD=
 LDFLAGS=
 LD_OHASH=
 LDADD=
 LDFLAGS=
 LD_OHASH=
-LD_SQLITE3=
 STATIC="-static"
 
 STATIC="-static"
 
-BUILD_DB=1
 BUILD_CGI=0
 
 HAVE_DIRENT_NAMLEN=
 BUILD_CGI=0
 
 HAVE_DIRENT_NAMLEN=
@@ -53,7 +51,6 @@ HAVE_GETLINE=
 HAVE_GETSUBOPT=
 HAVE_ISBLANK=
 HAVE_MKDTEMP=
 HAVE_GETSUBOPT=
 HAVE_ISBLANK=
 HAVE_MKDTEMP=
-HAVE_MMAP=
 HAVE_PLEDGE=
 HAVE_PROGNAME=
 HAVE_REALLOCARRAY=
 HAVE_PLEDGE=
 HAVE_PROGNAME=
 HAVE_REALLOCARRAY=
@@ -70,8 +67,6 @@ HAVE_STRTONUM=
 HAVE_VASPRINTF=
 HAVE_WCHAR=
 
 HAVE_VASPRINTF=
 HAVE_WCHAR=
 
-HAVE_SQLITE3=
-HAVE_SQLITE3_ERRSTR=
 HAVE_OHASH=
 HAVE_MANPATH=
 
 HAVE_OHASH=
 HAVE_MANPATH=
 
@@ -186,7 +181,6 @@ runtest getline             GETLINE         || true
 runtest getsubopt      GETSUBOPT       || true
 runtest isblank                ISBLANK         || true
 runtest mkdtemp                MKDTEMP         || true
 runtest getsubopt      GETSUBOPT       || true
 runtest isblank                ISBLANK         || true
 runtest mkdtemp                MKDTEMP         || true
-runtest mmap           MMAP            || true
 runtest pledge         PLEDGE          || true
 runtest sandbox_init   SANDBOX_INIT    || true
 runtest progname       PROGNAME        || true
 runtest pledge         PLEDGE          || true
 runtest sandbox_init   SANDBOX_INIT    || true
 runtest progname       PROGNAME        || true
@@ -203,44 +197,6 @@ runtest strtonum   STRTONUM        || true
 runtest vasprintf      VASPRINTF       || true
 runtest wchar          WCHAR           || true
 
 runtest vasprintf      VASPRINTF       || true
 runtest wchar          WCHAR           || true
 
-# --- sqlite3 ---
-if [ ${BUILD_DB} -eq 0 ]; then
-       echo "BUILD_DB=0 (manual)" 1>&2
-       echo "BUILD_DB=0 (manual)" 1>&3
-       echo 1>&3
-       HAVE_SQLITE3=0
-elif ismanual sqlite3 "${HAVE_SQLITE3}"; then
-       if [ -z "${LD_SQLITE3}" ]; then
-               LD_SQLITE3="-lsqlite3"
-       fi
-elif [ -n "${LD_SQLITE3}" ]; then
-       runtest sqlite3 SQLITE3 "${LD_SQLITE3}" || true
-elif singletest sqlite3 SQLITE3 "-lsqlite3"; then
-       LD_SQLITE3="-lsqlite3"
-elif runtest sqlite3 SQLITE3 \
-               "-I/usr/local/include -L/usr/local/lib -lsqlite3"; then
-       LD_SQLITE3="-L/usr/local/lib -lsqlite3"
-       CFLAGS="${CFLAGS} -I/usr/local/include"
-fi
-if [ ${HAVE_SQLITE3} -eq 0 ]; then
-       LD_SQLITE3=
-       if [ ${BUILD_DB} -gt 0 ]; then
-               echo "BUILD_DB=0 (no sqlite3)" 1>&2
-               echo "BUILD_DB=0 (no sqlite3)" 1>&3
-               echo 1>&3
-               BUILD_DB=0
-       fi
-fi
-
-# --- sqlite3_errstr ---
-if [ ${BUILD_DB} -eq 0 ]; then
-       HAVE_SQLITE3_ERRSTR=1
-elif ismanual sqlite3_errstr "${HAVE_SQLITE3_ERRSTR}"; then
-       :
-else
-       runtest sqlite3_errstr SQLITE3_ERRSTR "${LD_SQLITE3}" || true
-fi
-
 # --- ohash ---
 if ismanual ohash "${HAVE_OHASH}"; then
        :
 # --- ohash ---
 if ismanual ohash "${HAVE_OHASH}"; then
        :
@@ -256,7 +212,7 @@ if [ "${HAVE_OHASH}" -eq 0 ]; then
 fi
 
 # --- LDADD ---
 fi
 
 # --- LDADD ---
-LDADD="${LDADD} ${LD_SQLITE3} ${LD_OHASH} -lz"
+LDADD="${LDADD} ${LD_OHASH} -lz"
 echo "LDADD=\"${LDADD}\"" 1>&2
 echo "LDADD=\"${LDADD}\"" 1>&3
 echo 1>&3
 echo "LDADD=\"${LDADD}\"" 1>&2
 echo "LDADD=\"${LDADD}\"" 1>&3
 echo 1>&3
@@ -315,7 +271,6 @@ cat << __HEREDOC__
 #define HAVE_GETSUBOPT ${HAVE_GETSUBOPT}
 #define HAVE_ISBLANK ${HAVE_ISBLANK}
 #define HAVE_MKDTEMP ${HAVE_MKDTEMP}
 #define HAVE_GETSUBOPT ${HAVE_GETSUBOPT}
 #define HAVE_ISBLANK ${HAVE_ISBLANK}
 #define HAVE_MKDTEMP ${HAVE_MKDTEMP}
-#define HAVE_MMAP ${HAVE_MMAP}
 #define HAVE_PLEDGE ${HAVE_PLEDGE}
 #define HAVE_PROGNAME ${HAVE_PROGNAME}
 #define HAVE_REALLOCARRAY ${HAVE_REALLOCARRAY}
 #define HAVE_PLEDGE ${HAVE_PLEDGE}
 #define HAVE_PROGNAME ${HAVE_PROGNAME}
 #define HAVE_REALLOCARRAY ${HAVE_REALLOCARRAY}
@@ -331,8 +286,6 @@ cat << __HEREDOC__
 #define HAVE_STRTONUM ${HAVE_STRTONUM}
 #define HAVE_VASPRINTF ${HAVE_VASPRINTF}
 #define HAVE_WCHAR ${HAVE_WCHAR}
 #define HAVE_STRTONUM ${HAVE_STRTONUM}
 #define HAVE_VASPRINTF ${HAVE_VASPRINTF}
 #define HAVE_WCHAR ${HAVE_WCHAR}
-#define HAVE_SQLITE3 ${HAVE_SQLITE3}
-#define HAVE_SQLITE3_ERRSTR ${HAVE_SQLITE3_ERRSTR}
 #define HAVE_OHASH ${HAVE_OHASH}
 #define HAVE_MANPATH ${HAVE_MANPATH}
 
 #define HAVE_OHASH ${HAVE_OHASH}
 #define HAVE_MANPATH ${HAVE_MANPATH}
 
@@ -371,9 +324,6 @@ fi
 [ ${HAVE_REALLOCARRAY} -eq 0 ] && \
        echo "extern    void     *reallocarray(void *, size_t, size_t);"
 
 [ ${HAVE_REALLOCARRAY} -eq 0 ] && \
        echo "extern    void     *reallocarray(void *, size_t, size_t);"
 
-[ ${BUILD_DB} -gt 0 -a ${HAVE_SQLITE3_ERRSTR} -eq 0 ] &&
-       echo "extern    const char *sqlite3_errstr(int);"
-
 [ ${HAVE_STRCASESTR} -eq 0 ] && \
        echo "extern    char     *strcasestr(const char *, const char *);"
 
 [ ${HAVE_STRCASESTR} -eq 0 ] && \
        echo "extern    char     *strcasestr(const char *, const char *);"
 
@@ -413,17 +363,10 @@ exec > Makefile.local
 [ -z "${INSTALL_MAN}"     ] && INSTALL_MAN="${INSTALL} -m 0444"
 [ -z "${INSTALL_DATA}"    ] && INSTALL_DATA="${INSTALL} -m 0444"
 
 [ -z "${INSTALL_MAN}"     ] && INSTALL_MAN="${INSTALL} -m 0444"
 [ -z "${INSTALL_DATA}"    ] && INSTALL_DATA="${INSTALL} -m 0444"
 
-if [ ${BUILD_DB} -eq 0 -a ${BUILD_CGI} -gt 0 ]; then
-       echo "BUILD_CGI=0 (no BUILD_DB)" 1>&2
-       echo "BUILD_CGI=0 (no BUILD_DB)" 1>&3
-       BUILD_CGI=0
-fi
-
-BUILD_TARGETS="base-build"
-[ ${BUILD_CGI} -gt 0 ] && BUILD_TARGETS="${BUILD_TARGETS} cgi-build"
-INSTALL_TARGETS="base-install"
-[ ${BUILD_DB}  -gt 0 ] && INSTALL_TARGETS="${INSTALL_TARGETS} db-install"
-[ ${BUILD_CGI} -gt 0 ] && INSTALL_TARGETS="${INSTALL_TARGETS} cgi-install"
+BUILD_TARGETS=
+[ ${BUILD_CGI} -gt 0 ] && BUILD_TARGETS="cgi-build"
+INSTALL_TARGETS=
+[ ${BUILD_CGI} -gt 0 ] && INSTALL_TARGETS="cgi-install"
 
 cat << __HEREDOC__
 BUILD_TARGETS  = ${BUILD_TARGETS}
 
 cat << __HEREDOC__
 BUILD_TARGETS  = ${BUILD_TARGETS}
@@ -460,9 +403,6 @@ INSTALL_MAN = ${INSTALL_MAN}
 INSTALL_DATA   = ${INSTALL_DATA}
 __HEREDOC__
 
 INSTALL_DATA   = ${INSTALL_DATA}
 __HEREDOC__
 
-[ ${BUILD_DB} -gt 0 ] && \
-       echo "MAIN_OBJS = \$(BASE_OBJS) \$(DB_OBJS)"
-
 echo "Makefile.local: written" 1>&2
 echo "Makefile.local: written" 1>&3
 
 echo "Makefile.local: written" 1>&2
 echo "Makefile.local: written" 1>&3
 
index 87a005ab5fb0c09c981588722166e2a4c0881b9d..6103f99e1f5ad31f087bd9fab620ecc7c437157e 100644 (file)
@@ -1,4 +1,4 @@
-# $Id: configure.local.example,v 1.14 2016/07/19 17:56:04 schwarze Exp $
+# $Id: configure.local.example,v 1.15 2016/07/19 21:31:55 schwarze Exp $
 #
 # Copyright (c) 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org>
 #
 #
 # Copyright (c) 2014, 2015, 2016 Ingo Schwarze <schwarze@openbsd.org>
 #
@@ -65,7 +65,7 @@ MANPATH_DEFAULT="/usr/share/man:/usr/X11R6/man:/usr/local/man"
 # If you do not want uname(3) to be called but instead want a fixed
 # string to be used, use the following line:
 
 # If you do not want uname(3) to be called but instead want a fixed
 # string to be used, use the following line:
 
-OSNAME="OpenBSD 5.9"
+OSNAME="OpenBSD 6.0"
 
 # The following installation directories are used.
 # It is possible to set only one or a few of these variables,
 
 # The following installation directories are used.
 # It is possible to set only one or a few of these variables,
@@ -111,14 +111,20 @@ MANM_ROFF="mandoc_roff"           # default is "roff"
 MANM_EQN="mandoc_eqn"          # default is "eqn"
 MANM_TBL="mandoc_tbl"          # default is "tbl"
 
 MANM_EQN="mandoc_eqn"          # default is "eqn"
 MANM_TBL="mandoc_tbl"          # default is "tbl"
 
-# Some distributions may want to avoid naming conflicts
-# with other man(1) and soelim(1) utilities.
+# Some distributions may want to avoid naming conflicts with
+# other man(1), apropos(1), makewhatis(8), or soelim(1) utilities.
 # If you want to change the names of binary programs,
 # the following alternative names are suggested.
 # Using different names is possible as well.
 # If you want to change the names of binary programs,
 # the following alternative names are suggested.
 # Using different names is possible as well.
-# This changes the names of the installed section 1 manual pages as well.
+# This changes the names of the installed section 1 and section 8
+# manual pages as well.
+# It is possible to set only one or two of these variables,
+# there is no need to copy the whole block.
 
 BINM_MAN=mman                  # default is "man"
 
 BINM_MAN=mman                  # default is "man"
+BINM_APROPOS=mapropos          # default is "apropos"
+BINM_WHATIS=mwhatis            # default is "whatis"
+BINM_MAKEWHATIS=mandocdb       # default is "makewhatis"
 BINM_SOELIM=msoelim            # default is "soelim"
 
 # Before falling back to the bundled version of the ohash(3) hashing
 BINM_SOELIM=msoelim            # default is "soelim"
 
 # Before falling back to the bundled version of the ohash(3) hashing
@@ -129,6 +135,13 @@ BINM_SOELIM=msoelim                # default is "soelim"
 
 LD_OHASH="-lutil"
 
 
 LD_OHASH="-lutil"
 
+# When library autodetection decides to use -L/usr/local/lib,
+# -I/usr/local/include is automatically added to CFLAGS.
+# If you manually set LD_OHASH to something including -L/usr/local/lib,
+# chances are you will also need the following line:
+
+CFLAGS="${CFLAGS} -I/usr/local/include"
+
 # Some platforms may need additional linker flags to link against libmandoc
 # that are not autodetected.
 # For example, Solaris 9 and 10 need -lrt for nanosleep(2).
 # Some platforms may need additional linker flags to link against libmandoc
 # that are not autodetected.
 # For example, Solaris 9 and 10 need -lrt for nanosleep(2).
@@ -150,43 +163,6 @@ INSTALL_LIB="${INSTALL} -m 0444"
 INSTALL_MAN="${INSTALL} -m 0444"
 INSTALL_DATA="${INSTALL} -m 0444"
 
 INSTALL_MAN="${INSTALL} -m 0444"
 INSTALL_DATA="${INSTALL} -m 0444"
 
-# --- user settings related to database support ------------------------
-
-# By default, building makewhatis(8) and apropos(1) is enabled.
-# To disable it, for example to avoid the dependency on SQLite3,
-# use the following line.  It that case, the remaining settings
-# in this section are irrelevant.
-
-BUILD_DB=0
-
-# Autoconfiguration tries the following linker flags to find the
-# SQLite3 library installed on your system.  If none of these work,
-# set the following variable to specify the required linker flags.
-
-LD_SQLITE3="-lsqlite3"
-LD_SQLITE3="-L/usr/local/lib -lsqlite3"
-
-# When library autodetection decides to use -L/usr/local/lib,
-# -I/usr/local/include is automatically added to CFLAGS.
-# If you manually set LD_SQLITE3 to something including -L/usr/local/lib,
-# chances are you will also need the following line:
-
-CFLAGS="${CFLAGS} -I/usr/local/include"
-
-# Some distributions may want to avoid naming conflicts
-# with another implementation of apropos(1) and makewhatis(8).
-# If you want to change the names of the binary programs,
-# the following alternative names are suggested.
-# Using other names is possible as well.
-# This changes the names of the installed section 1 and section 8
-# manual pages as well.
-# It is possible to set only one or two of these variables,
-# there is no need to copy the whole block.
-
-BINM_APROPOS=mapropos          # default is "apropos"
-BINM_WHATIS=mwhatis            # default is "whatis"
-BINM_MAKEWHATIS=mandocdb       # default is "makewhatis"
-
 # When using the "homebrew" package manager on Mac OS X, the actual
 # manuals are located in a so-called "cellar" and only symlinked
 # into the manual trees.  To allow mandoc to follow such symlinks,
 # When using the "homebrew" package manager on Mac OS X, the actual
 # manuals are located in a so-called "cellar" and only symlinked
 # into the manual trees.  To allow mandoc to follow such symlinks,
@@ -200,7 +176,6 @@ HOMEBREWDIR="${PREFIX}/Cellar"
 
 # By default, building man.cgi(8) is disabled.  To enable it, copy
 # cgi.h.example to cgi.h, edit it, and use the following line.
 
 # By default, building man.cgi(8) is disabled.  To enable it, copy
 # cgi.h.example to cgi.h, edit it, and use the following line.
-# Obviously, this requires that BUILD_DB is enabled, too.
 
 BUILD_CGI=1
 
 
 BUILD_CGI=1
 
@@ -258,7 +233,7 @@ HAVE_GETLINE=0
 HAVE_GETSUBOPT=0
 HAVE_ISBLANK=0
 HAVE_MKDTEMP=0
 HAVE_GETSUBOPT=0
 HAVE_ISBLANK=0
 HAVE_MKDTEMP=0
-HAVE_MMAP=0
+HAVE_OHASH=0
 HAVE_PLEDGE=0
 HAVE_PROGNAME=0
 HAVE_REALLOCARRAY=0
 HAVE_PLEDGE=0
 HAVE_PROGNAME=0
 HAVE_REALLOCARRAY=0
@@ -273,7 +248,3 @@ HAVE_STRSEP=0
 HAVE_STRTONUM=0
 HAVE_VASPRINTF=0
 HAVE_WCHAR=0
 HAVE_STRTONUM=0
 HAVE_VASPRINTF=0
 HAVE_WCHAR=0
-
-HAVE_SQLITE3=0
-HAVE_SQLITE3_ERRSTR=0
-HAVE_OHASH=0
diff --git a/dba.c b/dba.c
new file mode 100644 (file)
index 0000000..a9a4878
--- /dev/null
+++ b/dba.c
@@ -0,0 +1,418 @@
+/*     $Id: dba.c,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Allocation-based version of the mandoc database, for read-write access.
+ * The interface is defined in "dba.h".
+ */
+#include <sys/types.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mandoc_aux.h"
+#include "mansearch.h"
+#include "dba_write.h"
+#include "dba_array.h"
+#include "dba.h"
+
+static void    *prepend(const char *, char);
+static void     dba_pages_write(struct dba_array *);
+static int      compare_names(const void *, const void *);
+static void     dba_macros_write(struct dba_array *);
+static void     dba_macro_write(struct dba_array *);
+
+
+/*** top-level functions **********************************************/
+
+struct dba *
+dba_new(int32_t npages)
+{
+       struct dba      *dba;
+       int32_t          im;
+
+       dba = mandoc_malloc(sizeof(*dba));
+       dba->pages = dba_array_new(npages, DBA_GROW);
+       dba->macros = dba_array_new(MACRO_MAX, 0);
+       for (im = 0; im < MACRO_MAX; im++)
+               dba_array_set(dba->macros, im, dba_array_new(128, DBA_GROW));
+       return dba;
+}
+
+void
+dba_free(struct dba *dba)
+{
+       struct dba_array        *page, *macro, *entry;
+
+       dba_array_FOREACH(dba->macros, macro) {
+               dba_array_undel(macro);
+               dba_array_FOREACH(macro, entry) {
+                       free(dba_array_get(entry, 0));
+                       dba_array_free(dba_array_get(entry, 1));
+                       dba_array_free(entry);
+               }
+               dba_array_free(macro);
+       }
+       dba_array_free(dba->macros);
+
+       dba_array_undel(dba->pages);
+       dba_array_FOREACH(dba->pages, page) {
+               dba_array_free(dba_array_get(page, DBP_NAME));
+               dba_array_free(dba_array_get(page, DBP_SECT));
+               dba_array_free(dba_array_get(page, DBP_ARCH));
+               free(dba_array_get(page, DBP_DESC));
+               dba_array_free(dba_array_get(page, DBP_FILE));
+               dba_array_free(page);
+       }
+       dba_array_free(dba->pages);
+
+       free(dba);
+}
+
+/*
+ * Write the complete mandoc database to disk; the format is:
+ * - One integer each for magic and version.
+ * - One pointer each to the macros table and to the final magic.
+ * - The pages table.
+ * - The macros table.
+ * - And at the very end, the magic integer again.
+ */
+int
+dba_write(const char *fname, struct dba *dba)
+{
+       int      save_errno;
+       int32_t  pos_end, pos_macros, pos_macros_ptr;
+
+       if (dba_open(fname) == -1)
+               return -1;
+       dba_int_write(MANDOCDB_MAGIC);
+       dba_int_write(MANDOCDB_VERSION);
+       pos_macros_ptr = dba_skip(1, 2);
+       dba_pages_write(dba->pages);
+       pos_macros = dba_tell();
+       dba_macros_write(dba->macros);
+       pos_end = dba_tell();
+       dba_int_write(MANDOCDB_MAGIC);
+       dba_seek(pos_macros_ptr);
+       dba_int_write(pos_macros);
+       dba_int_write(pos_end);
+       if (dba_close() == -1) {
+               save_errno = errno;
+               unlink(fname);
+               errno = save_errno;
+               return -1;
+       }
+       return 0;
+}
+
+
+/*** functions for handling pages *************************************/
+
+/*
+ * Create a new page and append it to the pages table.
+ */
+struct dba_array *
+dba_page_new(struct dba_array *pages, const char *name, const char *sect,
+    const char *arch, const char *desc, const char *file, enum form form)
+{
+       struct dba_array *page, *entry;
+
+       page = dba_array_new(DBP_MAX, 0);
+       entry = dba_array_new(1, DBA_STR | DBA_GROW);
+       dba_array_add(entry, prepend(name, NAME_FILE & NAME_MASK));
+       dba_array_add(page, entry);
+       entry = dba_array_new(1, DBA_STR | DBA_GROW);
+       dba_array_add(entry, (void *)sect);
+       dba_array_add(page, entry);
+       if (arch != NULL && *arch != '\0') {
+               entry = dba_array_new(1, DBA_STR | DBA_GROW);
+               dba_array_add(entry, (void *)arch);
+       } else
+               entry = NULL;
+       dba_array_add(page, entry);
+       dba_array_add(page, mandoc_strdup(desc));
+       entry = dba_array_new(1, DBA_STR | DBA_GROW);
+       dba_array_add(entry, prepend(file, form));
+       dba_array_add(page, entry);
+       dba_array_add(pages, page);
+       return page;
+}
+
+/*
+ * Add a section, architecture, or file name to an existing page.
+ * Passing the NULL pointer for the architecture makes the page MI.
+ * In that case, any earlier or later architectures are ignored.
+ */
+void
+dba_page_add(struct dba_array *page, int32_t ie, const char *str)
+{
+       struct dba_array        *entries;
+       char                    *entry;
+
+       entries = dba_array_get(page, ie);
+       if (ie == DBP_ARCH) {
+               if (entries == NULL)
+                       return;
+               if (str == NULL) {
+                       dba_array_free(entries);
+                       dba_array_set(page, DBP_ARCH, NULL);
+                       return;
+               }
+       }
+       if (*str == '\0')
+               return;
+       dba_array_FOREACH(entries, entry)
+               if (strcmp(entry, str) == 0)
+                       return;
+       dba_array_add(entries, (void *)str);
+}
+
+/*
+ * Add an additional name to an existing page.
+ */
+void
+dba_page_alias(struct dba_array *page, const char *name, uint64_t mask)
+{
+       struct dba_array        *entries;
+       char                    *entry;
+       char                     maskbyte;
+
+       if (*name == '\0')
+               return;
+       maskbyte = mask & NAME_MASK;
+       entries = dba_array_get(page, DBP_NAME);
+       dba_array_FOREACH(entries, entry) {
+               if (strcmp(entry + 1, name) == 0) {
+                       *entry |= maskbyte;
+                       return;
+               }
+       }
+       dba_array_add(entries, prepend(name, maskbyte));
+}
+
+/*
+ * Return a pointer to a temporary copy of instr with inbyte prepended.
+ */
+static void *
+prepend(const char *instr, char inbyte)
+{
+       static char     *outstr = NULL;
+       static size_t    outlen = 0;
+       size_t           newlen;
+
+       newlen = strlen(instr) + 1;
+       if (newlen > outlen) {
+               outstr = mandoc_realloc(outstr, newlen + 1);
+               outlen = newlen;
+       }
+       *outstr = inbyte;
+       memcpy(outstr + 1, instr, newlen);
+       return outstr;
+}
+
+/*
+ * Write the pages table to disk; the format is:
+ * - One integer containing the number of pages.
+ * - For each page, five pointers to the names, sections,
+ *   architectures, description, and file names of the page.
+ *   MI pages write 0 instead of the architecture pointer.
+ * - One list each for names, sections, architectures, descriptions and
+ *   file names.  The description for each page ends with a NUL byte.
+ *   For all the other lists, each string ends with a NUL byte,
+ *   and the last string for a page ends with two NUL bytes.
+ * - To assure alignment of following integers,
+ *   the end is padded with NUL bytes up to a multiple of four bytes.
+ */
+static void
+dba_pages_write(struct dba_array *pages)
+{
+       struct dba_array        *page, *names;
+       void                    *entry;
+       int32_t                  pos_pages, pos_end;
+
+       pos_pages = dba_array_writelen(pages, 5);
+       dba_array_FOREACH(pages, page) {
+               dba_array_setpos(page, DBP_NAME, dba_tell());
+               names = dba_array_get(page, DBP_NAME);
+               dba_array_sort(names, compare_names);
+               dba_array_writelst(names);
+       }
+       dba_array_FOREACH(pages, page) {
+               dba_array_setpos(page, DBP_SECT, dba_tell());
+               dba_array_writelst(dba_array_get(page, DBP_SECT));
+       }
+       dba_array_FOREACH(pages, page) {
+               if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) {
+                       dba_array_setpos(page, DBP_ARCH, dba_tell());
+                       dba_array_writelst(entry);
+               } else
+                       dba_array_setpos(page, DBP_ARCH, 0);
+       }
+       dba_array_FOREACH(pages, page) {
+               dba_array_setpos(page, DBP_DESC, dba_tell());
+               dba_str_write(dba_array_get(page, DBP_DESC));
+       }
+       dba_array_FOREACH(pages, page) {
+               dba_array_setpos(page, DBP_FILE, dba_tell());
+               dba_array_writelst(dba_array_get(page, DBP_FILE));
+       }
+       pos_end = dba_align();
+       dba_seek(pos_pages);
+       dba_array_FOREACH(pages, page)
+               dba_array_writepos(page);
+       dba_seek(pos_end);
+}
+
+static int
+compare_names(const void *vp1, const void *vp2)
+{
+       const char      *cp1, *cp2;
+       int              diff;
+
+       cp1 = *(char **)vp1;
+       cp2 = *(char **)vp2;
+       return (diff = *cp2 - *cp1) ? diff :
+           strcasecmp(cp1 + 1, cp2 + 1);
+}
+
+
+/*** functions for handling macros ************************************/
+
+/*
+ * Create a new macro entry and append it to one of the macro tables.
+ */
+void
+dba_macro_new(struct dba *dba, int32_t im, const char *value,
+    const int32_t *pp)
+{
+       struct dba_array        *entry, *pages;
+       const int32_t           *ip;
+       int32_t                  np;
+
+       np = 0;
+       for (ip = pp; *ip; ip++)
+               np++;
+       pages = dba_array_new(np, DBA_GROW);
+       for (ip = pp; *ip; ip++)
+               dba_array_add(pages, dba_array_get(dba->pages,
+                   be32toh(*ip) / 5 / sizeof(*ip) - 1));
+
+       entry = dba_array_new(2, 0);
+       dba_array_add(entry, mandoc_strdup(value));
+       dba_array_add(entry, pages);
+
+       dba_array_add(dba_array_get(dba->macros, im), entry);
+}
+
+/*
+ * Look up a macro entry by value and add a reference to a new page to it.
+ * If the value does not yet exist, create a new macro entry
+ * and add it to the macro table in question.
+ */
+void
+dba_macro_add(struct dba_array *macros, int32_t im, const char *value,
+    struct dba_array *page)
+{
+       struct dba_array        *macro, *entry, *pages;
+
+       if (*value == '\0')
+               return;
+       macro = dba_array_get(macros, im);
+       dba_array_FOREACH(macro, entry)
+               if (strcmp(value, dba_array_get(entry, 0)) == 0)
+                       break;
+       if (entry == NULL) {
+               entry = dba_array_new(2, 0);
+               dba_array_add(entry, mandoc_strdup(value));
+               pages = dba_array_new(1, DBA_GROW);
+               dba_array_add(entry, pages);
+               dba_array_add(macro, entry);
+       } else
+               pages = dba_array_get(entry, 1);
+       dba_array_add(pages, page);
+}
+
+/*
+ * Write the macros table to disk; the format is:
+ * - The number of macro tables (actually, MACRO_MAX).
+ * - That number of pointers to the individual macro tables.
+ * - The individual macro tables.
+ */
+static void
+dba_macros_write(struct dba_array *macros)
+{
+       struct dba_array        *macro;
+       int32_t                  im, pos_macros, pos_end;
+
+       pos_macros = dba_array_writelen(macros, 1);
+       im = 0;
+       dba_array_FOREACH(macros, macro) {
+               dba_array_setpos(macros, im++, dba_tell());
+               dba_macro_write(macro);
+       }
+       pos_end = dba_tell();
+       dba_seek(pos_macros);
+       dba_array_writepos(macros);
+       dba_seek(pos_end);
+}
+
+/*
+ * Write one individual macro table to disk; the format is:
+ * - The number of entries in the table.
+ * - For each entry, two pointers, the first one to the value
+ *   and the second one to the list of pages.
+ * - A list of values, each ending in a NUL byte.
+ * - To assure alignment of following integers,
+ *   padding with NUL bytes up to a multiple of four bytes.
+ * - A list of pointers to pages, each list ending in a 0 integer.
+ */
+static void
+dba_macro_write(struct dba_array *macro)
+{
+       struct dba_array        *entry, *pages, *page;
+       int                      empty;
+       int32_t                  addr, pos_macro, pos_end;
+
+       dba_array_FOREACH(macro, entry) {
+               pages = dba_array_get(entry, 1);
+               empty = 1;
+               dba_array_FOREACH(pages, page)
+                       if (dba_array_getpos(page))
+                               empty = 0;
+               if (empty)
+                       dba_array_del(macro);
+       }
+       pos_macro = dba_array_writelen(macro, 2);
+       dba_array_FOREACH(macro, entry) {
+               dba_array_setpos(entry, 0, dba_tell());
+               dba_str_write(dba_array_get(entry, 0));
+       }
+       dba_align();
+       dba_array_FOREACH(macro, entry) {
+               dba_array_setpos(entry, 1, dba_tell());
+               pages = dba_array_get(entry, 1);
+               dba_array_FOREACH(pages, page)
+                       if ((addr = dba_array_getpos(page)))
+                               dba_int_write(addr);
+               dba_int_write(0);
+       }
+       pos_end = dba_tell();
+       dba_seek(pos_macro);
+       dba_array_FOREACH(macro, entry)
+               dba_array_writepos(entry);
+       dba_seek(pos_end);
+}
diff --git a/dba.h b/dba.h
new file mode 100644 (file)
index 0000000..6715472
--- /dev/null
+++ b/dba.h
@@ -0,0 +1,51 @@
+/*     $Id: dba.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Public interface of the allocation-based version
+ * of the mandoc database, for read-write access.
+ * To be used by dba.c, dba_read.c, and makewhatis(8).
+ */
+
+#define        DBP_NAME        0
+#define        DBP_SECT        1
+#define        DBP_ARCH        2
+#define        DBP_DESC        3
+#define        DBP_FILE        4
+#define        DBP_MAX         5
+
+struct dba_array;
+
+struct dba {
+       struct dba_array        *pages;
+       struct dba_array        *macros;
+};
+
+
+struct dba     *dba_new(int32_t);
+void            dba_free(struct dba *);
+struct dba     *dba_read(const char *);
+int             dba_write(const char *, struct dba *);
+
+struct dba_array *dba_page_new(struct dba_array *, const char *,
+                       const char *, const char *, const char *,
+                       const char *, enum form);
+void            dba_page_add(struct dba_array *, int32_t, const char *);
+void            dba_page_alias(struct dba_array *, const char *, uint64_t);
+
+void            dba_macro_new(struct dba *, int32_t,
+                       const char *, const int32_t *);
+void            dba_macro_add(struct dba_array *, int32_t,
+                       const char *, struct dba_array *);
diff --git a/dba_array.c b/dba_array.c
new file mode 100644 (file)
index 0000000..18c9f09
--- /dev/null
@@ -0,0 +1,188 @@
+/*     $Id: dba_array.c,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Allocation-based arrays for the mandoc database, for read-write access.
+ * The interface is defined in "dba_array.h".
+ */
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mandoc_aux.h"
+#include "dba_write.h"
+#include "dba_array.h"
+
+struct dba_array {
+       void    **ep;   /* Array of entries. */
+       int32_t  *em;   /* Array of map positions. */
+       int       flags;
+       int32_t   ea;   /* Entries allocated. */
+       int32_t   eu;   /* Entries used (including deleted). */
+       int32_t   ed;   /* Entries deleted. */
+       int32_t   ec;   /* Currently active entry. */
+       int32_t   pos;  /* Map position of this array. */
+};
+
+
+struct dba_array *
+dba_array_new(int32_t ea, int flags)
+{
+       struct dba_array        *array;
+
+       assert(ea > 0);
+       array = mandoc_malloc(sizeof(*array));
+       array->ep = mandoc_reallocarray(NULL, ea, sizeof(*array->ep));
+       array->em = mandoc_reallocarray(NULL, ea, sizeof(*array->em));
+       array->ea = ea;
+       array->eu = 0;
+       array->ed = 0;
+       array->ec = 0;
+       array->flags = flags;
+       array->pos = 0;
+       return array;
+}
+
+void
+dba_array_free(struct dba_array *array)
+{
+       int32_t  ie;
+
+       if (array == NULL)
+               return;
+       if (array->flags & DBA_STR)
+               for (ie = 0; ie < array->eu; ie++)
+                       free(array->ep[ie]);
+       free(array->ep);
+       free(array->em);
+       free(array);
+}
+
+void
+dba_array_set(struct dba_array *array, int32_t ie, void *entry)
+{
+       assert(ie >= 0);
+       assert(ie < array->ea);
+       assert(ie <= array->eu);
+       if (ie == array->eu)
+               array->eu++;
+       if (array->flags & DBA_STR)
+               entry = mandoc_strdup(entry);
+       array->ep[ie] = entry;
+       array->em[ie] = 0;
+}
+
+void
+dba_array_add(struct dba_array *array, void *entry)
+{
+       if (array->eu == array->ea) {
+               assert(array->flags & DBA_GROW);
+               array->ep = mandoc_reallocarray(array->ep,
+                   2, sizeof(*array->ep) * array->ea);
+               array->em = mandoc_reallocarray(array->em,
+                   2, sizeof(*array->em) * array->ea);
+               array->ea *= 2;
+       }
+       dba_array_set(array, array->eu, entry);
+}
+
+void *
+dba_array_get(struct dba_array *array, int32_t ie)
+{
+       if (ie < 0 || ie >= array->eu || array->em[ie] == -1)
+               return NULL;
+       return array->ep[ie];
+}
+
+void
+dba_array_start(struct dba_array *array)
+{
+       array->ec = array->eu;
+}
+
+void *
+dba_array_next(struct dba_array *array)
+{
+       if (array->ec < array->eu)
+               array->ec++;
+       else
+               array->ec = 0;
+       while (array->ec < array->eu && array->em[array->ec] == -1)
+               array->ec++;
+       return array->ec < array->eu ? array->ep[array->ec] : NULL;
+}
+
+void
+dba_array_del(struct dba_array *array)
+{
+       if (array->ec < array->eu && array->em[array->ec] != -1) {
+               array->em[array->ec] = -1;
+               array->ed++;
+       }
+}
+
+void
+dba_array_undel(struct dba_array *array)
+{
+       memset(array->em, 0, sizeof(*array->em) * array->eu);
+}
+
+void
+dba_array_setpos(struct dba_array *array, int32_t ie, int32_t pos)
+{
+       array->em[ie] = pos;
+}
+
+int32_t
+dba_array_getpos(struct dba_array *array)
+{
+       return array->pos;
+}
+
+void
+dba_array_sort(struct dba_array *array, dba_compare_func func)
+{
+       assert(array->ed == 0);
+       qsort(array->ep, array->eu, sizeof(*array->ep), func);
+}
+
+int32_t
+dba_array_writelen(struct dba_array *array, int32_t nmemb)
+{
+       dba_int_write(array->eu - array->ed);
+       return dba_skip(nmemb, array->eu - array->ed);
+}
+
+void
+dba_array_writepos(struct dba_array *array)
+{
+       int32_t  ie;
+
+       array->pos = dba_tell();
+       for (ie = 0; ie < array->eu; ie++)
+               if (array->em[ie] != -1)
+                       dba_int_write(array->em[ie]);
+}
+
+void
+dba_array_writelst(struct dba_array *array)
+{
+       const char      *str;
+
+       dba_array_FOREACH(array, str)
+               dba_str_write(str);
+       dba_char_write('\0');
+}
diff --git a/dba_array.h b/dba_array.h
new file mode 100644 (file)
index 0000000..d9a6ee6
--- /dev/null
@@ -0,0 +1,47 @@
+/*     $Id: dba_array.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Public interface for allocation-based arrays
+ * for the mandoc database, for read-write access.
+ * To be used by dba*.c and by makewhatis(8).
+ */
+
+struct dba_array;
+
+#define        DBA_STR         0x01    /* Map contains strings, not pointers. */
+#define        DBA_GROW        0x02    /* Allow the array to grow. */
+
+#define        dba_array_FOREACH(a, e) \
+       dba_array_start(a); \
+       while (((e) = dba_array_next(a)) != NULL)
+
+typedef int dba_compare_func(const void *, const void *);
+
+struct dba_array *dba_array_new(int32_t, int);
+void            dba_array_free(struct dba_array *);
+void            dba_array_set(struct dba_array *, int32_t, void *);
+void            dba_array_add(struct dba_array *, void *);
+void           *dba_array_get(struct dba_array *, int32_t);
+void            dba_array_start(struct dba_array *);
+void           *dba_array_next(struct dba_array *);
+void            dba_array_del(struct dba_array *);
+void            dba_array_undel(struct dba_array *);
+void            dba_array_setpos(struct dba_array *, int32_t, int32_t);
+int32_t                 dba_array_getpos(struct dba_array *);
+void            dba_array_sort(struct dba_array *, dba_compare_func);
+int32_t                 dba_array_writelen(struct dba_array *, int32_t);
+void            dba_array_writepos(struct dba_array *);
+void            dba_array_writelst(struct dba_array *);
diff --git a/dba_read.c b/dba_read.c
new file mode 100644 (file)
index 0000000..67d8d86
--- /dev/null
@@ -0,0 +1,74 @@
+/*     $Id: dba_read.c,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Function to read the mandoc database from disk into RAM,
+ * such that data can be added or removed.
+ * The interface is defined in "dba.h".
+ * This file is seperate from dba.c because this also uses "dbm.h".
+ */
+#include <regex.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+
+#include "mandoc_aux.h"
+#include "mansearch.h"
+#include "dba_array.h"
+#include "dba.h"
+#include "dbm.h"
+
+
+struct dba *
+dba_read(const char *fname)
+{
+       struct dba              *dba;
+       struct dba_array        *page;
+       struct dbm_page         *pdata;
+       struct dbm_macro        *mdata;
+       const char              *cp;
+       int32_t                  im, ip, iv, npages;
+
+       if (dbm_open(fname) == -1)
+               return NULL;
+       npages = dbm_page_count();
+       dba = dba_new(npages);
+       for (ip = 0; ip < npages; ip++) {
+               pdata = dbm_page_get(ip);
+               page = dba_page_new(dba->pages, pdata->name, pdata->sect,
+                   pdata->arch, pdata->desc, pdata->file + 1, *pdata->file);
+               cp = pdata->name;
+               while (*(cp = strchr(cp, '\0') + 1) != '\0')
+                       dba_page_add(page, DBP_NAME, cp);
+               cp = pdata->sect;
+               while (*(cp = strchr(cp, '\0') + 1) != '\0')
+                       dba_page_add(page, DBP_SECT, cp);
+               if ((cp = pdata->arch) != NULL)
+                       while (*(cp = strchr(cp, '\0') + 1) != '\0')
+                               dba_page_add(page, DBP_ARCH, cp);
+               cp = pdata->file;
+               while (*(cp = strchr(cp, '\0') + 1) != '\0')
+                       dba_page_add(page, DBP_FILE, cp);
+       }
+       for (im = 0; im < MACRO_MAX; im++) {
+               for (iv = 0; iv < dbm_macro_count(im); iv++) {
+                       mdata = dbm_macro_get(im, iv);
+                       dba_macro_new(dba, im, mdata->value, mdata->pp);
+               }
+       }
+       dbm_close();
+       return dba;
+}
diff --git a/dba_write.c b/dba_write.c
new file mode 100644 (file)
index 0000000..15a04fc
--- /dev/null
@@ -0,0 +1,117 @@
+/*     $Id: dba_write.c,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Low-level functions for serializing allocation-based data to disk.
+ * The interface is defined in "dba_write.h".
+ */
+#include <assert.h>
+#include <endian.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+
+#include "dba_write.h"
+
+static FILE    *ofp;
+
+
+int
+dba_open(const char *fname)
+{
+       ofp = fopen(fname, "w");
+       return ofp == NULL ? -1 : 0;
+}
+
+int
+dba_close(void)
+{
+       return fclose(ofp) == EOF ? -1 : 0;
+}
+
+int32_t
+dba_tell(void)
+{
+       long             pos;
+
+       if ((pos = ftell(ofp)) == -1)
+               err(1, "ftell");
+       if (pos >= INT32_MAX) {
+               errno = EOVERFLOW;
+               err(1, "ftell = %ld", pos);
+       }
+       return pos;
+}
+
+void
+dba_seek(int32_t pos)
+{
+       if (fseek(ofp, pos, SEEK_SET) == -1)
+               err(1, "fseek(%d)", pos);
+}
+
+int32_t
+dba_align(void)
+{
+       int32_t          pos;
+
+       pos = dba_tell();
+       while (pos & 3) {
+               dba_char_write('\0');
+               pos++;
+       }
+       return pos;
+}
+
+int32_t
+dba_skip(int32_t nmemb, int32_t sz)
+{
+       const int32_t    out[5] = {0, 0, 0, 0, 0};
+       int32_t          i, pos;
+
+       assert(sz >= 0);
+       assert(nmemb > 0);
+       assert(nmemb <= 5);
+       pos = dba_tell();
+       for (i = 0; i < sz; i++)
+               if (nmemb - fwrite(&out, sizeof(out[0]), nmemb, ofp))
+                       err(1, "fwrite");
+       return pos;
+}
+
+void
+dba_char_write(int c)
+{
+       if (putc(c, ofp) == EOF)
+               err(1, "fputc");
+}
+
+void
+dba_str_write(const char *str)
+{
+       if (fputs(str, ofp) == EOF)
+               err(1, "fputs");
+       dba_char_write('\0');
+}
+
+void
+dba_int_write(int32_t i)
+{
+       i = htobe32(i);
+       if (fwrite(&i, sizeof(i), 1, ofp) != 1)
+               err(1, "fwrite");
+}
diff --git a/dba_write.h b/dba_write.h
new file mode 100644 (file)
index 0000000..6adda05
--- /dev/null
@@ -0,0 +1,30 @@
+/*     $Id: dba_write.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Internal interface to low-level functions
+ * for serializing allocation-based data to disk.
+ * For use by dba_array.c and dba.c only.
+ */
+
+int     dba_open(const char *);
+int     dba_close(void);
+int32_t         dba_tell(void);
+void    dba_seek(int32_t);
+int32_t         dba_align(void);
+int32_t         dba_skip(int32_t, int32_t);
+void    dba_char_write(int);
+void    dba_str_write(const char *);
+void    dba_int_write(int32_t);
diff --git a/dbm.c b/dbm.c
new file mode 100644 (file)
index 0000000..d4e69bc
--- /dev/null
+++ b/dbm.c
@@ -0,0 +1,452 @@
+/*     $Id: dbm.c,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Map-based version of the mandoc database, for read-only access.
+ * The interface is defined in "dbm.h".
+ */
+#include <assert.h>
+#include <endian.h>
+#include <err.h>
+#include <errno.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include "mansearch.h"
+#include "dbm_map.h"
+#include "dbm.h"
+
+struct macro {
+       int32_t value;
+       int32_t pages;
+};
+
+struct page {
+       int32_t name;
+       int32_t sect;
+       int32_t arch;
+       int32_t desc;
+       int32_t file;
+};
+
+enum iter {
+       ITER_NONE = 0,
+       ITER_NAME,
+       ITER_SECT,
+       ITER_ARCH,
+       ITER_DESC,
+       ITER_MACRO
+};
+
+static struct macro    *macros[MACRO_MAX];
+static int32_t          nvals[MACRO_MAX];
+static struct page     *pages;
+static int32_t          npages;
+static enum iter        iteration;
+
+static struct dbm_res   page_bytitle(enum iter, const struct dbm_match *);
+static struct dbm_res   page_byarch(const struct dbm_match *);
+static struct dbm_res   page_bymacro(int32_t, const struct dbm_match *);
+static char            *macro_bypage(int32_t, int32_t);
+
+
+/*** top level functions **********************************************/
+
+/*
+ * Open a disk-based mandoc database for read-only access.
+ * Map the pages and macros[] arrays.
+ * Return 0 on success.  Return -1 and set errno on failure.
+ */
+int
+dbm_open(const char *fname)
+{
+       const int32_t   *mp, *ep;
+       int32_t          im;
+
+       if (dbm_map(fname) == -1)
+               return -1;
+
+       if ((npages = be32toh(*dbm_getint(4))) < 0) {
+               warnx("dbm_open(%s): Invalid number of pages: %d",
+                   fname, npages);
+               goto fail;
+       }
+       pages = (struct page *)dbm_getint(5);
+
+       if ((mp = dbm_get(*dbm_getint(2))) == NULL) {
+               warnx("dbm_open(%s): Invalid offset of macros array", fname);
+               goto fail;
+       }
+       if (be32toh(*mp) != MACRO_MAX) {
+               warnx("dbm_open(%s): Invalid number of macros: %d",
+                   fname, be32toh(*mp));
+               goto fail;
+       }
+       for (im = 0; im < MACRO_MAX; im++) {
+               if ((ep = dbm_get(*++mp)) == NULL) {
+                       warnx("dbm_open(%s): Invalid offset of macro %d",
+                           fname, im);
+                       goto fail;
+               }
+               nvals[im] = be32toh(*ep);
+               macros[im] = (struct macro *)++ep;
+       }
+       return 0;
+
+fail:
+       dbm_unmap();
+       errno = EFTYPE;
+       return -1;
+}
+
+void
+dbm_close(void)
+{
+       dbm_unmap();
+}
+
+
+/*** functions for handling pages *************************************/
+
+int32_t
+dbm_page_count(void)
+{
+       return npages;
+}
+
+/*
+ * Give the caller pointers to the data for one manual page.
+ */
+struct dbm_page *
+dbm_page_get(int32_t ip)
+{
+       static struct dbm_page   res;
+
+       assert(ip >= 0);
+       assert(ip < npages);
+       res.name = dbm_get(pages[ip].name);
+       res.sect = dbm_get(pages[ip].sect);
+       res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
+       res.desc = dbm_get(pages[ip].desc);
+       res.file = dbm_get(pages[ip].file);
+       res.addr = dbm_addr(pages + ip);
+       return &res;
+}
+
+/*
+ * Functions to start filtered iterations over manual pages.
+ */
+void
+dbm_page_byname(const struct dbm_match *match)
+{
+       assert(match != NULL);
+       page_bytitle(ITER_NAME, match);
+}
+
+void
+dbm_page_bysect(const struct dbm_match *match)
+{
+       assert(match != NULL);
+       page_bytitle(ITER_SECT, match);
+}
+
+void
+dbm_page_byarch(const struct dbm_match *match)
+{
+       assert(match != NULL);
+       page_byarch(match);
+}
+
+void
+dbm_page_bydesc(const struct dbm_match *match)
+{
+       assert(match != NULL);
+       page_bytitle(ITER_DESC, match);
+}
+
+void
+dbm_page_bymacro(int32_t im, const struct dbm_match *match)
+{
+       assert(im >= 0);
+       assert(im < MACRO_MAX);
+       assert(match != NULL);
+       page_bymacro(im, match);
+}
+
+/*
+ * Return the number of the next manual page in the current iteration.
+ */
+struct dbm_res
+dbm_page_next(void)
+{
+       struct dbm_res                   res = {-1, 0};
+
+       switch(iteration) {
+       case ITER_NONE:
+               return res;
+       case ITER_ARCH:
+               return page_byarch(NULL);
+       case ITER_MACRO:
+               return page_bymacro(0, NULL);
+       default:
+               return page_bytitle(iteration, NULL);
+       }
+}
+
+/*
+ * Functions implementing the iteration over manual pages.
+ */
+static struct dbm_res
+page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match)
+{
+       static const struct dbm_match   *match;
+       static const char               *cp;    
+       static int32_t                   ip;
+       struct dbm_res                   res = {-1, 0};
+
+       assert(arg_iter == ITER_NAME || arg_iter == ITER_DESC ||
+           arg_iter == ITER_SECT);
+
+       /* Initialize for a new iteration. */
+
+       if (arg_match != NULL) {
+               iteration = arg_iter;
+               match = arg_match;
+               switch (iteration) {
+               case ITER_NAME:
+                       cp = dbm_get(pages[0].name);
+                       break;
+               case ITER_SECT:
+                       cp = dbm_get(pages[0].sect);
+                       break;
+               case ITER_DESC:
+                       cp = dbm_get(pages[0].desc);
+                       break;
+               default:
+                       abort();
+               }
+               ip = 0;
+               return res;
+       }
+
+       /* Search for a name. */
+
+       while (ip < npages) {
+               if (iteration == ITER_NAME)
+                       cp++;
+               if (dbm_match(match, cp))
+                       break;
+               cp = strchr(cp, '\0') + 1;
+               if (iteration == ITER_DESC)
+                       ip++;
+               else if (*cp == '\0') {
+                       cp++;
+                       ip++;
+               }
+       }
+
+       /* Reached the end without a match. */
+
+       if (ip == npages) {
+               iteration = ITER_NONE;
+               match = NULL;
+               cp = NULL;
+               return res;
+       }
+
+       /* Found a match; save the quality for later retrieval. */
+
+       res.page = ip;
+       res.bits = iteration == ITER_NAME ? cp[-1] : 0;
+
+       /* Skip the remaining names of this page. */
+
+       if (++ip < npages) {
+               do {
+                       cp++;
+               } while (cp[-1] != '\0' ||
+                   (iteration != ITER_DESC && cp[-2] != '\0'));
+       }
+       return res;
+}
+
+static struct dbm_res
+page_byarch(const struct dbm_match *arg_match)
+{
+       static const struct dbm_match   *match;
+       struct dbm_res                   res = {-1, 0};
+       static int32_t                   ip;
+       const char                      *cp;    
+
+       /* Initialize for a new iteration. */
+
+       if (arg_match != NULL) {
+               iteration = ITER_ARCH;
+               match = arg_match;
+               ip = 0;
+               return res;
+       }
+
+       /* Search for an architecture. */
+
+       for ( ; ip < npages; ip++)
+               if (pages[ip].arch)
+                       for (cp = dbm_get(pages[ip].arch);
+                           *cp != '\0';
+                           cp = strchr(cp, '\0') + 1)
+                               if (dbm_match(match, cp)) {
+                                       res.page = ip++;
+                                       return res;
+                               }
+
+       /* Reached the end without a match. */
+
+       iteration = ITER_NONE;
+       match = NULL;
+       return res;
+}
+
+static struct dbm_res
+page_bymacro(int32_t im, const struct dbm_match *match)
+{
+       static const int32_t    *pp;
+       struct dbm_res           res = {-1, 0};
+       const char              *cp;
+       int32_t                  iv;
+
+       assert(im >= 0);
+       assert(im < MACRO_MAX);
+
+       /* Initialize for a new iteration. */
+
+       if (match != NULL) {
+               iteration = ITER_MACRO;
+               cp = nvals[im] ? dbm_get(macros[im]->value) : NULL;
+               for (iv = 0; iv < nvals[im]; iv++) {
+                       if (dbm_match(match, cp))
+                               break;
+                       cp = strchr(cp, '\0') + 1;
+               }
+               pp = iv == nvals[im] ? NULL : dbm_get(macros[im][iv].pages);
+               return res;
+       }
+       if (iteration != ITER_MACRO)
+               return res;
+
+       /* No more matches. */
+
+       if (pp == NULL || *pp == 0) {
+               iteration = ITER_NONE;
+               pp = NULL;
+               return res;
+       }
+
+       /* Found a match. */
+
+       res.page = (struct page *)dbm_get(*pp++) - pages;
+       return res;
+}
+
+
+/*** functions for handling macros ************************************/
+
+int32_t
+dbm_macro_count(int32_t im)
+{
+       assert(im >= 0);
+       assert(im < MACRO_MAX);
+       return nvals[im];
+}
+
+struct dbm_macro *
+dbm_macro_get(int32_t im, int32_t iv)
+{
+       static struct dbm_macro macro;
+
+       assert(im >= 0);
+       assert(im < MACRO_MAX);
+       assert(iv >= 0);
+       assert(iv < nvals[im]);
+       macro.value = dbm_get(macros[im][iv].value);
+       macro.pp = dbm_get(macros[im][iv].pages);
+       return &macro;
+}
+
+/*
+ * Filtered iteration over macro entries.
+ */
+void
+dbm_macro_bypage(int32_t im, int32_t ip)
+{
+       assert(im >= 0);
+       assert(im < MACRO_MAX);
+       assert(ip != 0);
+       macro_bypage(im, ip);
+}
+
+char *
+dbm_macro_next(void)
+{
+       return macro_bypage(MACRO_MAX, 0);
+}
+
+static char *
+macro_bypage(int32_t arg_im, int32_t arg_ip)
+{
+       static const int32_t    *pp;
+       static int32_t           im, ip, iv;
+
+       /* Initialize for a new iteration. */
+
+       if (arg_im < MACRO_MAX && arg_ip != 0) {
+               im = arg_im;
+               ip = arg_ip;
+               pp = dbm_get(macros[im]->pages);
+               iv = 0;
+               return NULL;
+       }
+       if (im >= MACRO_MAX)
+               return NULL;
+
+       /* Search for the next value. */
+
+       while (iv < nvals[im]) {
+               if (*pp == ip)
+                       break;
+               if (*pp == 0)
+                       iv++;
+               pp++;
+       }
+
+       /* Reached the end without a match. */
+
+       if (iv == nvals[im]) {
+               im = MACRO_MAX;
+               ip = 0;
+               pp = NULL;
+               return NULL;
+       }
+
+       /* Found a match; skip the remaining pages of this entry. */
+
+       if (++iv < nvals[im])
+               while (*pp++ != 0)
+                       continue;
+
+       return dbm_get(macros[im][iv - 1].value);
+}
diff --git a/dbm.h b/dbm.h
new file mode 100644 (file)
index 0000000..ec2cd47
--- /dev/null
+++ b/dbm.h
@@ -0,0 +1,68 @@
+/*     $Id: dbm.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Public interface for the map-based version
+ * of the mandoc database, for read-only access.
+ * To be used by dbm*.c, dba_read.c, and man(1) and apropos(1).
+ */
+
+enum dbm_mtype {
+       DBM_EXACT = 0,
+       DBM_SUB,
+       DBM_REGEX
+};
+
+struct dbm_match {
+       regex_t         *re;
+       const char      *str;
+       enum dbm_mtype   type;
+};
+
+struct dbm_res {
+       int32_t          page;
+       int32_t          bits;
+};
+
+struct dbm_page {
+       const char      *name;
+       const char      *sect;
+       const char      *arch;
+       const char      *desc;
+       const char      *file;
+       int32_t          addr;
+};
+
+struct dbm_macro {
+       const char      *value;
+       const int32_t   *pp;
+};
+
+int             dbm_open(const char *);
+void            dbm_close(void);
+
+int32_t                 dbm_page_count(void);
+struct dbm_page        *dbm_page_get(int32_t);
+void            dbm_page_byname(const struct dbm_match *);
+void            dbm_page_bysect(const struct dbm_match *);
+void            dbm_page_byarch(const struct dbm_match *);
+void            dbm_page_bydesc(const struct dbm_match *);
+void            dbm_page_bymacro(int32_t, const struct dbm_match *);
+struct dbm_res  dbm_page_next(void);
+
+int32_t                 dbm_macro_count(int32_t);
+struct dbm_macro *dbm_macro_get(int32_t, int32_t);
+void            dbm_macro_bypage(int32_t, int32_t);
+char           *dbm_macro_next(void);
diff --git a/dbm_map.c b/dbm_map.c
new file mode 100644 (file)
index 0000000..486bb29
--- /dev/null
+++ b/dbm_map.c
@@ -0,0 +1,174 @@
+/*     $Id: dbm_map.c,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
+/*
+ * Copyright (c) 2016 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.
+ *
+ * Low-level routines for the map-based version
+ * of the mandoc database, for read-only access.
+ * The interface is defined in "dbm_map.h".
+ */
+#include <sys/mman.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+
+#include <endian.h>
+#include <err.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <regex.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include "mansearch.h"
+#include "dbm_map.h"
+#include "dbm.h"
+
+static struct stat      st;
+static char            *dbm_base;
+static int              ifd;
+static int32_t          max_offset;
+
+/*
+ * Open a disk-based database for read-only access.
+ * Validate the file format as far as it is not mandoc-specific.
+ * Return 0 on success.  Return -1 and set errno on failure.
+ */
+int
+dbm_map(const char *fname)
+{
+       int              save_errno;
+       const int32_t   *magic;
+
+       if ((ifd = open(fname, O_RDONLY)) == -1)
+               return -1;
+       if (fstat(ifd, &st) == -1)
+               goto fail;
+       if (st.st_size < 5) {
+               warnx("dbm_map(%s): File too short", fname);
+               errno = EFTYPE;
+               goto fail;
+       }
+       if (st.st_size > INT32_MAX) {
+               errno = EFBIG;
+               goto fail;
+       }
+       if ((dbm_base = mmap(NULL, st.st_size, PROT_READ, MAP_SHARED,
+           ifd, 0)) == MAP_FAILED)
+               goto fail;
+       magic = dbm_getint(0);
+       if (be32toh(*magic) != MANDOCDB_MAGIC) {
+               warnx("dbm_map(%s): Bad initial magic %x (expected %x)",
+                   fname, be32toh(*magic), MANDOCDB_MAGIC);
+               errno = EFTYPE;
+               goto fail;
+       }
+       magic = dbm_getint(1);
+       if (be32toh(*magic) != MANDOCDB_VERSION) {
+               warnx("dbm_map(%s): Bad version number %d (expected %d)",
+                   fname, be32toh(*magic), MANDOCDB_VERSION);
+               errno = EFTYPE;
+               goto fail;
+       }
+       max_offset = be32toh(*dbm_getint(3)) + sizeof(int32_t);
+       if (st.st_size != max_offset) {
+               warnx("dbm_map(%s): Inconsistent file size %llu (expected %d)",
+                   fname, st.st_size, max_offset);
+               errno = EFTYPE;
+               goto fail;
+       }
+       if ((magic = dbm_get(*dbm_getint(3))) == NULL) {
+               errno = EFTYPE;
+               goto fail;
+       }
+       if (be32toh(*magic) != MANDOCDB_MAGIC) {
+               warnx("dbm_map(%s): Bad final magic %x (expected %x)",
+                   fname, be32toh(*magic), MANDOCDB_MAGIC);
+               errno = EFTYPE;
+               goto fail;
+       }
+       return 0;
+
+fail:
+       save_errno = errno;
+       close(ifd);
+       errno = save_errno;
+       return -1;
+}
+
+void
+dbm_unmap(void)
+{
+       if (munmap(dbm_base, st.st_size) == -1)
+               warn("dbm_unmap: munmap");
+       if (close(ifd) == -1)
+               warn("dbm_unmap: close");
+       dbm_base = (char *)-1;
+}
+
+/*
+ * Take a raw integer as it was read from the database.
+ * Interpret it as an offset into the database file
+ * and return a pointer to that place in the file.
+ */
+void *
+dbm_get(int32_t offset)
+{
+       offset = be32toh(offset);
+       if (offset < 0 || offset >= max_offset) {
+               warnx("dbm_get: Database corrupt: offset %d > %d",
+                   offset, max_offset);
+               return NULL;
+       }
+       return dbm_base + offset;
+}
+
+/*
+ * Assume the database starts with some integers.
+ * Assume they are numbered starting from 0, increasing.
+ * Get a pointer to one with the number "offset".
+ */
+int32_t *
+dbm_getint(int32_t offset)
+{
+       return (int32_t *)dbm_base + offset;
+}
+
+/*
+ * The reverse of dbm_get().
+ * Take pointer into the database file
+ * and convert it to the raw integer
+ * that would be used to refer to that place in the file.
+ */
+int32_t
+dbm_addr(const void *p)
+{
+       return htobe32((char *)p - dbm_base);
+}
+
+int
+dbm_match(const struct dbm_match *match, const char *str)
+{
+       switch (match->type) {
+       case DBM_EXACT:
+               return strcmp(str, match->str) == 0;
+       case DBM_SUB:
+               return strcasestr(str, match->str) != NULL;
+       case DBM_REGEX:
+               return regexec(match->re, str, 0, NULL, 0) == 0;
+       default:
+               abort();
+       }
+}
similarity index 57%
rename from mansearch_const.c
rename to dbm_map.h
index 61351c3c2a478d9fb283e59b3335dca8658b6013..9768fc5f2dcce1e593702d238ff66d13d1967891 100644 (file)
+++ b/dbm_map.h
@@ -1,6 +1,6 @@
-/*     $Id: mansearch_const.c,v 1.7 2014/12/01 08:05:52 schwarze Exp $ */
+/*     $Id: dbm_map.h,v 1.1 2016/07/19 21:31:55 schwarze Exp $ */
 /*
 /*
- * Copyright (c) 2014 Ingo Schwarze <schwarze@openbsd.org>
+ * Copyright (c) 2016 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
  *
  * Permission to use, copy, modify, and distribute this software for any
  * purpose with or without fee is hereby granted, provided that the above
  * 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.
  * 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.
+ *
+ * Private interface for low-level routines for the map-based version 
+ * of the mandoc database, for read-only access.
+ * To be used by dbm*.c only.
  */
  */
-#include "config.h"
-
-#include <sys/types.h>
-
-#include <stdint.h>
-
-#include "mansearch.h"
 
 
-const int mansearch_keymax = 40;
+struct dbm_match;
 
 
-const char *const mansearch_keynames[40] = {
-       "arch", "sec",  "Xr",   "Ar",   "Fa",   "Fl",   "Dv",   "Fn",
-       "Ic",   "Pa",   "Cm",   "Li",   "Em",   "Cd",   "Va",   "Ft",
-       "Tn",   "Er",   "Ev",   "Sy",   "Sh",   "In",   "Ss",   "Ox",
-       "An",   "Mt",   "St",   "Bx",   "At",   "Nx",   "Fx",   "Lk",
-       "Ms",   "Bsx",  "Dx",   "Rs",   "Vt",   "Lb",   "Nm",   "Nd"
-};
+int             dbm_map(const char *);
+void            dbm_unmap(void);
+void           *dbm_get(int32_t);
+int32_t                *dbm_getint(int32_t);
+int32_t                 dbm_addr(const void *);
+int             dbm_match(const struct dbm_match *, const char *);
diff --git a/main.c b/main.c
index 16f55a0923e84fd3d04c38e8a253624bcd3d496d..56bfd63543ec4e70c6b36a8536720ef558ba27cd 100644 (file)
--- a/main.c
+++ b/main.c
@@ -1,4 +1,4 @@
-/*     $Id: main.c,v 1.273 2016/07/19 13:36:13 schwarze Exp $ */
+/*     $Id: main.c,v 1.274 2016/07/19 21:31:55 schwarze Exp $ */
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2012, 2014-2016 Ingo Schwarze <schwarze@openbsd.org>
 /*
  * Copyright (c) 2008-2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2012, 2014-2016 Ingo Schwarze <schwarze@openbsd.org>
@@ -82,9 +82,7 @@ struct        curparse {
 };
 
 
 };
 
 
-#if HAVE_SQLITE3
 int                      mandocdb(int, char *[]);
 int                      mandocdb(int, char *[]);
-#endif
 
 static int               fs_lookup(const struct manpaths *,
                                size_t ipath, const char *,
 
 static int               fs_lookup(const struct manpaths *,
                                size_t ipath, const char *,
@@ -147,11 +145,9 @@ main(int argc, char *argv[])
        setprogname(progname);
 #endif
 
        setprogname(progname);
 #endif
 
-#if HAVE_SQLITE3
        if (strncmp(progname, "mandocdb", 8) == 0 ||
            strcmp(progname, BINM_MAKEWHATIS) == 0)
                return mandocdb(argc, argv);
        if (strncmp(progname, "mandocdb", 8) == 0 ||
            strcmp(progname, BINM_MAKEWHATIS) == 0)
                return mandocdb(argc, argv);
-#endif
 
 #if HAVE_PLEDGE
        if (pledge("stdio rpath tmppath tty proc exec flock", NULL) == -1)
 
 #if HAVE_PLEDGE
        if (pledge("stdio rpath tmppath tty proc exec flock", NULL) == -1)
@@ -349,9 +345,6 @@ main(int argc, char *argv[])
        /* man(1), whatis(1), apropos(1) */
 
        if (search.argmode != ARG_FILE) {
        /* man(1), whatis(1), apropos(1) */
 
        if (search.argmode != ARG_FILE) {
-               if (argc == 0)
-                       usage(search.argmode);
-
                if (search.argmode == ARG_NAME &&
                    outmode == OUTMODE_ONE)
                        search.firstmatch = 1;
                if (search.argmode == ARG_NAME &&
                    outmode == OUTMODE_ONE)
                        search.firstmatch = 1;
@@ -359,19 +352,9 @@ main(int argc, char *argv[])
                /* Access the mandoc database. */
 
                manconf_parse(&conf, conf_file, defpaths, auxpaths);
                /* Access the mandoc database. */
 
                manconf_parse(&conf, conf_file, defpaths, auxpaths);
-#if HAVE_SQLITE3
-               mansearch_setup(1);
                if ( ! mansearch(&search, &conf.manpath,
                    argc, argv, &res, &sz))
                        usage(search.argmode);
                if ( ! mansearch(&search, &conf.manpath,
                    argc, argv, &res, &sz))
                        usage(search.argmode);
-#else
-               if (search.argmode != ARG_NAME) {
-                       fputs("mandoc: database support not compiled in\n",
-                           stderr);
-                       return (int)MANDOCLEVEL_BADARG;
-               }
-               sz = 0;
-#endif
 
                if (sz == 0) {
                        if (search.argmode == ARG_NAME)
 
                if (sz == 0) {
                        if (search.argmode == ARG_NAME)
@@ -474,7 +457,7 @@ main(int argc, char *argv[])
 
                        if (resp == NULL)
                                parse(&curp, fd, *argv);
 
                        if (resp == NULL)
                                parse(&curp, fd, *argv);
-                       else if (resp->form & FORM_SRC) {
+                       else if (resp->form == FORM_SRC) {
                                /* For .so only; ignore failure. */
                                chdir(conf.manpath.paths[resp->ipath]);
                                parse(&curp, fd, resp->file);
                                /* For .so only; ignore failure. */
                                chdir(conf.manpath.paths[resp->ipath]);
                                parse(&curp, fd, resp->file);
@@ -522,10 +505,7 @@ main(int argc, char *argv[])
 out:
        if (search.argmode != ARG_FILE) {
                manconf_free(&conf);
 out:
        if (search.argmode != ARG_FILE) {
                manconf_free(&conf);
-#if HAVE_SQLITE3
                mansearch_free(res, sz);
                mansearch_free(res, sz);
-               mansearch_setup(0);
-#endif
        }
 
        free(defos);
        }
 
        free(defos);
@@ -629,7 +609,8 @@ fs_lookup(const struct manpaths *paths, size_t ipath,
        glob_t           globinfo;
        struct manpage  *page;
        char            *file;
        glob_t           globinfo;
        struct manpage  *page;
        char            *file;
-       int              form, globres;
+       int              globres;
+       enum form        form;
 
        form = FORM_SRC;
        mandoc_asprintf(&file, "%s/man%s/%s.%s",
 
        form = FORM_SRC;
        mandoc_asprintf(&file, "%s/man%s/%s.%s",
@@ -667,10 +648,8 @@ fs_lookup(const struct manpaths *paths, size_t ipath,
                return 0;
 
 found:
                return 0;
 
 found:
-#if HAVE_SQLITE3
        warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s",
            name, sec, BINM_MAKEWHATIS, paths->paths[ipath]);
        warnx("outdated mandoc.db lacks %s(%s) entry, run %s %s",
            name, sec, BINM_MAKEWHATIS, paths->paths[ipath]);
-#endif
        *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage));
        page = *res + (*ressz - 1);
        page->file = file;
        *res = mandoc_reallocarray(*res, ++*ressz, sizeof(struct manpage));
        page = *res + (*ressz - 1);
        page->file = file;
index b885dd5cc72b9b6d60f412944cf71d592cfc5acf..9bea830c13e092faf6a9906b5dbd21f492c3d5ef 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: mandocdb.c,v 1.220 2016/07/19 13:36:13 schwarze Exp $ */
+/*     $Id: mandocdb.c,v 1.221 2016/07/19 21:31:55 schwarze Exp $ */
 /*
  * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011-2016 Ingo Schwarze <schwarze@openbsd.org>
 /*
  * Copyright (c) 2011, 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2011-2016 Ingo Schwarze <schwarze@openbsd.org>
@@ -37,6 +37,7 @@
 #if HAVE_SANDBOX_INIT
 #include <sandbox.h>
 #endif
 #if HAVE_SANDBOX_INIT
 #include <sandbox.h>
 #endif
+#include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdint.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -44,8 +45,6 @@
 #include <string.h>
 #include <unistd.h>
 
 #include <string.h>
 #include <unistd.h>
 
-#include <sqlite3.h>
-
 #include "mandoc_aux.h"
 #include "mandoc_ohash.h"
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "mandoc_ohash.h"
 #include "mandoc.h"
 #include "man.h"
 #include "manconf.h"
 #include "mansearch.h"
 #include "man.h"
 #include "manconf.h"
 #include "mansearch.h"
+#include "dba_array.h"
+#include "dba.h"
 
 
-extern int mansearch_keymax;
 extern const char *const mansearch_keynames[];
 
 extern const char *const mansearch_keynames[];
 
-#define        SQL_EXEC(_v) \
-       if (SQLITE_OK != sqlite3_exec(db, (_v), NULL, NULL, NULL)) \
-               say("", "%s: %s", (_v), sqlite3_errmsg(db))
-#define        SQL_BIND_TEXT(_s, _i, _v) \
-       if (SQLITE_OK != sqlite3_bind_text \
-               ((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \
-               say(mlink->file, "%s", sqlite3_errmsg(db))
-#define        SQL_BIND_INT(_s, _i, _v) \
-       if (SQLITE_OK != sqlite3_bind_int \
-               ((_s), (_i)++, (_v))) \
-               say(mlink->file, "%s", sqlite3_errmsg(db))
-#define        SQL_BIND_INT64(_s, _i, _v) \
-       if (SQLITE_OK != sqlite3_bind_int64 \
-               ((_s), (_i)++, (_v))) \
-               say(mlink->file, "%s", sqlite3_errmsg(db))
-#define SQL_STEP(_s) \
-       if (SQLITE_DONE != sqlite3_step((_s))) \
-               say(mlink->file, "%s", sqlite3_errmsg(db))
-
 enum   op {
        OP_DEFAULT = 0, /* new dbs from dir list or default config */
        OP_CONFFILE, /* new databases from custom config file */
 enum   op {
        OP_DEFAULT = 0, /* new dbs from dir list or default config */
        OP_CONFFILE, /* new databases from custom config file */
@@ -98,14 +79,14 @@ struct      inodev {
 
 struct mpage {
        struct inodev    inodev;  /* used for hashing routine */
 
 struct mpage {
        struct inodev    inodev;  /* used for hashing routine */
-       int64_t          pageid;  /* pageid in mpages SQL table */
+       struct dba_array *dba;
        char            *sec;     /* section from file content */
        char            *arch;    /* architecture from file content */
        char            *title;   /* title from file content */
        char            *desc;    /* description from file content */
        struct mlink    *mlinks;  /* singly linked list */
        char            *sec;     /* section from file content */
        char            *arch;    /* architecture from file content */
        char            *title;   /* title from file content */
        char            *desc;    /* description from file content */
        struct mlink    *mlinks;  /* singly linked list */
-       int              form;    /* format from file content */
        int              name_head_done;
        int              name_head_done;
+       enum form        form;    /* format from file content */
 };
 
 struct mlink {
 };
 
 struct mlink {
@@ -116,19 +97,9 @@ struct      mlink {
        char            *fsec;    /* section from file name suffix */
        struct mlink    *next;    /* singly linked list */
        struct mpage    *mpage;   /* parent */
        char            *fsec;    /* section from file name suffix */
        struct mlink    *next;    /* singly linked list */
        struct mpage    *mpage;   /* parent */
-       int              dform;   /* format from directory */
-       int              fform;   /* format from file name suffix */
        int              gzip;    /* filename has a .gz suffix */
        int              gzip;    /* filename has a .gz suffix */
-};
-
-enum   stmt {
-       STMT_DELETE_PAGE = 0,   /* delete mpage */
-       STMT_INSERT_PAGE,       /* insert mpage */
-       STMT_INSERT_LINK,       /* insert mlink */
-       STMT_INSERT_NAME,       /* insert name */
-       STMT_SELECT_NAME,       /* retrieve existing name flags */
-       STMT_INSERT_KEY,        /* insert parsed key */
-       STMT__MAX
+       enum form        dform;   /* format from directory */
+       enum form        fform;   /* format from file name suffix */
 };
 
 typedef        int (*mdoc_fp)(struct mpage *, const struct roff_meta *,
 };
 
 typedef        int (*mdoc_fp)(struct mpage *, const struct roff_meta *,
@@ -142,20 +113,17 @@ struct    mdoc_handler {
 
 int             mandocdb(int, char *[]);
 
 
 int             mandocdb(int, char *[]);
 
-static void     dbclose(int);
-static void     dbadd(struct mpage *);
+static void     dbadd(struct dba *, struct mpage *);
 static void     dbadd_mlink(const struct mlink *mlink);
 static void     dbadd_mlink(const struct mlink *mlink);
-static void     dbadd_mlink_name(const struct mlink *mlink);
-static int      dbopen(int);
-static void     dbprune(void);
+static void     dbprune(struct dba *);
+static void     dbwrite(struct dba *);
 static void     filescan(const char *);
 static void     mlink_add(struct mlink *, const struct stat *);
 static void     mlink_check(struct mpage *, struct mlink *);
 static void     mlink_free(struct mlink *);
 static void     mlinks_undupe(struct mpage *);
 static void     mpages_free(void);
 static void     filescan(const char *);
 static void     mlink_add(struct mlink *, const struct stat *);
 static void     mlink_check(struct mpage *, struct mlink *);
 static void     mlink_free(struct mlink *);
 static void     mlinks_undupe(struct mpage *);
 static void     mpages_free(void);
-static void     mpages_merge(struct mparse *);
-static void     names_check(void);
+static void     mpages_merge(struct dba *, struct mparse *);
 static void     parse_cat(struct mpage *, int);
 static void     parse_man(struct mpage *, const struct roff_meta *,
                        const struct roff_node *);
 static void     parse_cat(struct mpage *, int);
 static void     parse_man(struct mpage *, const struct roff_meta *,
                        const struct roff_node *);
@@ -191,7 +159,6 @@ static      int      set_basedir(const char *, int);
 static int      treescan(void);
 static size_t   utf8(unsigned int, char [7]);
 
 static int      treescan(void);
 static size_t   utf8(unsigned int, char [7]);
 
-static char             tempfilename[32];
 static int              nodb; /* no database changes */
 static int              mparse_options; /* abort the parse early */
 static int              use_all; /* use all found files */
 static int              nodb; /* no database changes */
 static int              mparse_options; /* abort the parse early */
 static int              use_all; /* use all found files */
@@ -205,8 +172,6 @@ static      struct ohash     mpages; /* table of distinct manual pages */
 static struct ohash     mlinks; /* table of directory entries */
 static struct ohash     names; /* table of all names */
 static struct ohash     strings; /* table of all strings */
 static struct ohash     mlinks; /* table of directory entries */
 static struct ohash     names; /* table of all names */
 static struct ohash     strings; /* table of all strings */
-static sqlite3         *db = NULL; /* current database */
-static sqlite3_stmt    *stmts[STMT__MAX]; /* current statements */
 static uint64_t         name_mask;
 
 static const struct mdoc_handler mdocs[MDOC_MAX] = {
 static uint64_t         name_mask;
 
 static const struct mdoc_handler mdocs[MDOC_MAX] = {
@@ -341,6 +306,7 @@ mandocdb(int argc, char *argv[])
 {
        struct manconf    conf;
        struct mparse    *mp;
 {
        struct manconf    conf;
        struct mparse    *mp;
+       struct dba       *dba;
        const char       *path_arg, *progname;
        size_t            j, sz;
        int               ch, i;
        const char       *path_arg, *progname;
        size_t            j, sz;
        int               ch, i;
@@ -360,7 +326,6 @@ mandocdb(int argc, char *argv[])
 #endif
 
        memset(&conf, 0, sizeof(conf));
 #endif
 
        memset(&conf, 0, sizeof(conf));
-       memset(stmts, 0, STMT__MAX * sizeof(sqlite3_stmt *));
 
        /*
         * We accept a few different invocations.
 
        /*
         * We accept a few different invocations.
@@ -461,7 +426,7 @@ mandocdb(int argc, char *argv[])
                if (OP_TEST != op && 0 == set_basedir(path_arg, 1))
                        goto out;
 
                if (OP_TEST != op && 0 == set_basedir(path_arg, 1))
                        goto out;
 
-               if (dbopen(1)) {
+               if ((dba = dba_read(MANDOC_DB)) != NULL) {
                        /*
                         * The existing database is usable.  Process
                         * all files specified on the command-line.
                        /*
                         * The existing database is usable.  Process
                         * all files specified on the command-line.
@@ -479,7 +444,7 @@ mandocdb(int argc, char *argv[])
                        for (i = 0; i < argc; i++)
                                filescan(argv[i]);
                        if (OP_TEST != op)
                        for (i = 0; i < argc; i++)
                                filescan(argv[i]);
                        if (OP_TEST != op)
-                               dbprune();
+                               dbprune(dba);
                } else {
                        /*
                         * Database missing or corrupt.
                } else {
                        /*
                         * Database missing or corrupt.
@@ -489,12 +454,13 @@ mandocdb(int argc, char *argv[])
                        op = OP_DEFAULT;
                        if (0 == treescan())
                                goto out;
                        op = OP_DEFAULT;
                        if (0 == treescan())
                                goto out;
-                       if (0 == dbopen(0))
-                               goto out;
+                       dba = dba_new(128);
                }
                if (OP_DELETE != op)
                }
                if (OP_DELETE != op)
-                       mpages_merge(mp);
-               dbclose(OP_DEFAULT == op ? 0 : 1);
+                       mpages_merge(dba, mp);
+               if (nodb == 0)
+                       dbwrite(dba);
+               dba_free(dba);
        } else {
                /*
                 * If we have arguments, use them as our manpaths.
        } else {
                /*
                 * If we have arguments, use them as our manpaths.
@@ -539,14 +505,11 @@ mandocdb(int argc, char *argv[])
                                continue;
                        if (0 == treescan())
                                continue;
                                continue;
                        if (0 == treescan())
                                continue;
-                       if (0 == dbopen(0))
-                               continue;
-
-                       mpages_merge(mp);
-                       if (warnings && !nodb &&
-                           ! (MPARSE_QUICK & mparse_options))
-                               names_check();
-                       dbclose(0);
+                       dba = dba_new(128);
+                       mpages_merge(dba, mp);
+                       if (nodb == 0)
+                               dbwrite(dba);
+                       dba_free(dba);
 
                        if (j + 1 < conf.manpath.sz) {
                                mpages_free();
 
                        if (j + 1 < conf.manpath.sz) {
                                mpages_free();
@@ -596,7 +559,8 @@ treescan(void)
        FTS             *f;
        FTSENT          *ff;
        struct mlink    *mlink;
        FTS             *f;
        FTSENT          *ff;
        struct mlink    *mlink;
-       int              dform, gzip;
+       int              gzip;
+       enum form        dform;
        char            *dsec, *arch, *fsec, *cp;
        const char      *path;
        const char      *argv[2];
        char            *dsec, *arch, *fsec, *cp;
        const char      *path;
        const char      *argv[2];
@@ -970,6 +934,7 @@ mlink_add(struct mlink *mlink, const struct stat *st)
                mpage = mandoc_calloc(1, sizeof(struct mpage));
                mpage->inodev.st_ino = inodev.st_ino;
                mpage->inodev.st_dev = inodev.st_dev;
                mpage = mandoc_calloc(1, sizeof(struct mpage));
                mpage->inodev.st_ino = inodev.st_ino;
                mpage->inodev.st_dev = inodev.st_dev;
+               mpage->form = FORM_NONE;
                ohash_insert(&mpages, slot, mpage);
        } else
                mlink->next = mpage->mlinks;
                ohash_insert(&mpages, slot, mpage);
        } else
                mlink->next = mpage->mlinks;
@@ -1118,7 +1083,7 @@ mlink_check(struct mpage *mpage, struct mlink *mlink)
  * and filename to determine whether the file is parsable or not.
  */
 static void
  * and filename to determine whether the file is parsable or not.
  */
 static void
-mpages_merge(struct mparse *mp)
+mpages_merge(struct dba *dba, struct mparse *mp)
 {
        char                     any[] = "any";
        struct mpage            *mpage, *mpage_dest;
 {
        char                     any[] = "any";
        struct mpage            *mpage, *mpage_dest;
@@ -1129,9 +1094,6 @@ mpages_merge(struct mparse *mp)
        int                      fd;
        unsigned int             pslot;
 
        int                      fd;
        unsigned int             pslot;
 
-       if ( ! nodb)
-               SQL_EXEC("BEGIN TRANSACTION");
-
        mpage = ohash_first(&mpages, &pslot);
        while (mpage != NULL) {
                mlinks_undupe(mpage);
        mpage = ohash_first(&mpages, &pslot);
        while (mpage != NULL) {
                mlinks_undupe(mpage);
@@ -1188,8 +1150,8 @@ mpages_merge(struct mparse *mp)
                                         * to the target.
                                         */
 
                                         * to the target.
                                         */
 
-                                       if (mpage_dest->pageid)
-                                               dbadd_mlink_name(mlink);
+                                       if (mpage_dest->dba != NULL)
+                                               dbadd_mlink(mlink);
 
                                        if (mlink->next == NULL)
                                                break;
 
                                        if (mlink->next == NULL)
                                                break;
@@ -1254,7 +1216,7 @@ mpages_merge(struct mparse *mp)
                             mlink = mlink->next)
                                mlink_check(mpage, mlink);
 
                             mlink = mlink->next)
                                mlink_check(mpage, mlink);
 
-               dbadd(mpage);
+               dbadd(dba, mpage);
                mlink = mpage->mlinks;
 
 nextpage:
                mlink = mpage->mlinks;
 
 nextpage:
@@ -1262,44 +1224,6 @@ nextpage:
                ohash_delete(&names);
                mpage = ohash_next(&mpages, &pslot);
        }
                ohash_delete(&names);
                mpage = ohash_next(&mpages, &pslot);
        }
-
-       if (0 == nodb)
-               SQL_EXEC("END TRANSACTION");
-}
-
-static void
-names_check(void)
-{
-       sqlite3_stmt    *stmt;
-       const char      *name, *sec, *arch, *key;
-
-       sqlite3_prepare_v2(db,
-         "SELECT name, sec, arch, key FROM ("
-           "SELECT name AS key, pageid FROM names "
-           "WHERE bits & ? AND NOT EXISTS ("
-             "SELECT pageid FROM mlinks "
-             "WHERE mlinks.pageid == names.pageid "
-             "AND mlinks.name == names.name"
-           ")"
-         ") JOIN ("
-           "SELECT sec, arch, name, pageid FROM mlinks "
-           "GROUP BY pageid"
-         ") USING (pageid);",
-         -1, &stmt, NULL);
-
-       if (sqlite3_bind_int64(stmt, 1, NAME_TITLE) != SQLITE_OK)
-               say("", "%s", sqlite3_errmsg(db));
-
-       while (sqlite3_step(stmt) == SQLITE_ROW) {
-               name = (const char *)sqlite3_column_text(stmt, 0);
-               sec  = (const char *)sqlite3_column_text(stmt, 1);
-               arch = (const char *)sqlite3_column_text(stmt, 2);
-               key  = (const char *)sqlite3_column_text(stmt, 3);
-               say("", "%s(%s%s%s) lacks mlink \"%s\"", name, sec,
-                   '\0' == *arch ? "" : "/",
-                   '\0' == *arch ? "" : arch, key);
-       }
-       sqlite3_finalize(stmt);
 }
 
 static void
 }
 
 static void
@@ -1823,7 +1747,7 @@ putkeys(const struct mpage *mpage, char *cp, size_t sz, uint64_t v)
        } else {
                htab = &strings;
                if (debug > 1)
        } else {
                htab = &strings;
                if (debug > 1)
-                   for (i = 0; i < mansearch_keymax; i++)
+                   for (i = 0; i < KEY_MAX; i++)
                        if ((uint64_t)1 << i & v)
                            say(mpage->mlinks->file,
                                "Adding key %s=%*s",
                        if ((uint64_t)1 << i & v)
                            say(mpage->mlinks->file,
                                "Adding key %s=%*s",
@@ -2029,53 +1953,23 @@ render_string(char **public, size_t *psz)
 static void
 dbadd_mlink(const struct mlink *mlink)
 {
 static void
 dbadd_mlink(const struct mlink *mlink)
 {
-       size_t           i;
-
-       i = 1;
-       SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->dsec);
-       SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->arch);
-       SQL_BIND_TEXT(stmts[STMT_INSERT_LINK], i, mlink->name);
-       SQL_BIND_INT64(stmts[STMT_INSERT_LINK], i, mlink->mpage->pageid);
-       SQL_STEP(stmts[STMT_INSERT_LINK]);
-       sqlite3_reset(stmts[STMT_INSERT_LINK]);
-}
-
-static void
-dbadd_mlink_name(const struct mlink *mlink)
-{
-       uint64_t         bits;
-       size_t           i;
-
-       dbadd_mlink(mlink);
-
-       i = 1;
-       SQL_BIND_INT64(stmts[STMT_SELECT_NAME], i, mlink->mpage->pageid);
-       bits = NAME_FILE & NAME_MASK;
-       if (sqlite3_step(stmts[STMT_SELECT_NAME]) == SQLITE_ROW) {
-               bits |= sqlite3_column_int64(stmts[STMT_SELECT_NAME], 0);
-               sqlite3_reset(stmts[STMT_SELECT_NAME]);
-       }
-
-       i = 1;
-       SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, bits);
-       SQL_BIND_TEXT(stmts[STMT_INSERT_NAME], i, mlink->name);
-       SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, mlink->mpage->pageid);
-       SQL_STEP(stmts[STMT_INSERT_NAME]);
-       sqlite3_reset(stmts[STMT_INSERT_NAME]);
+       dba_page_alias(mlink->mpage->dba, mlink->name, NAME_FILE);
+       dba_page_add(mlink->mpage->dba, DBP_SECT, mlink->dsec);
+       dba_page_add(mlink->mpage->dba, DBP_ARCH, mlink->arch);
+       dba_page_add(mlink->mpage->dba, DBP_FILE, mlink->file);
 }
 
 /*
  * Flush the current page's terms (and their bits) into the database.
 }
 
 /*
  * Flush the current page's terms (and their bits) into the database.
- * Wrap the entire set of additions in a transaction to make sqlite be a
- * little faster.
  * Also, handle escape sequences at the last possible moment.
  */
 static void
  * Also, handle escape sequences at the last possible moment.
  */
 static void
-dbadd(struct mpage *mpage)
+dbadd(struct dba *dba, struct mpage *mpage)
 {
        struct mlink    *mlink;
        struct str      *key;
        char            *cp;
 {
        struct mlink    *mlink;
        struct str      *key;
        char            *cp;
+       uint64_t         mask;
        size_t           i;
        unsigned int     slot;
        int              mustfree;
        size_t           i;
        unsigned int     slot;
        int              mustfree;
@@ -2120,111 +2014,87 @@ dbadd(struct mpage *mpage)
        cp = mpage->desc;
        i = strlen(cp);
        mustfree = render_string(&cp, &i);
        cp = mpage->desc;
        i = strlen(cp);
        mustfree = render_string(&cp, &i);
-       i = 1;
-       SQL_BIND_TEXT(stmts[STMT_INSERT_PAGE], i, cp);
-       SQL_BIND_INT(stmts[STMT_INSERT_PAGE], i, mpage->form);
-       SQL_STEP(stmts[STMT_INSERT_PAGE]);
-       mpage->pageid = sqlite3_last_insert_rowid(db);
-       sqlite3_reset(stmts[STMT_INSERT_PAGE]);
+       mpage->dba = dba_page_new(dba->pages, mlink->name,
+           mlink->dsec, mlink->arch, cp, mlink->file, mpage->form);
        if (mustfree)
                free(cp);
 
        if (mustfree)
                free(cp);
 
-       while (NULL != mlink) {
+       while ((mlink = mlink->next) != NULL)
                dbadd_mlink(mlink);
                dbadd_mlink(mlink);
-               mlink = mlink->next;
-       }
-       mlink = mpage->mlinks;
 
        for (key = ohash_first(&names, &slot); NULL != key;
             key = ohash_next(&names, &slot)) {
                assert(key->mpage == mpage);
 
        for (key = ohash_first(&names, &slot); NULL != key;
             key = ohash_next(&names, &slot)) {
                assert(key->mpage == mpage);
-               i = 1;
-               SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, key->mask);
-               SQL_BIND_TEXT(stmts[STMT_INSERT_NAME], i, key->key);
-               SQL_BIND_INT64(stmts[STMT_INSERT_NAME], i, mpage->pageid);
-               SQL_STEP(stmts[STMT_INSERT_NAME]);
-               sqlite3_reset(stmts[STMT_INSERT_NAME]);
+               dba_page_alias(mpage->dba, key->key, key->mask);
                free(key);
        }
        for (key = ohash_first(&strings, &slot); NULL != key;
             key = ohash_next(&strings, &slot)) {
                assert(key->mpage == mpage);
                free(key);
        }
        for (key = ohash_first(&strings, &slot); NULL != key;
             key = ohash_next(&strings, &slot)) {
                assert(key->mpage == mpage);
-               i = 1;
-               SQL_BIND_INT64(stmts[STMT_INSERT_KEY], i, key->mask);
-               SQL_BIND_TEXT(stmts[STMT_INSERT_KEY], i, key->key);
-               SQL_BIND_INT64(stmts[STMT_INSERT_KEY], i, mpage->pageid);
-               SQL_STEP(stmts[STMT_INSERT_KEY]);
-               sqlite3_reset(stmts[STMT_INSERT_KEY]);
+               i = 0;
+               for (mask = TYPE_Xr; mask <= TYPE_Lb; mask *= 2) {
+                       if (key->mask & mask)
+                               dba_macro_add(dba->macros, i,
+                                   key->key, mpage->dba);
+                       i++;
+               }
                free(key);
        }
 }
 
 static void
                free(key);
        }
 }
 
 static void
-dbprune(void)
+dbprune(struct dba *dba)
 {
 {
-       struct mpage    *mpage;
-       struct mlink    *mlink;
-       size_t           i;
-       unsigned int     slot;
-
-       if (0 == nodb)
-               SQL_EXEC("BEGIN TRANSACTION");
-
-       for (mpage = ohash_first(&mpages, &slot); NULL != mpage;
-            mpage = ohash_next(&mpages, &slot)) {
-               mlink = mpage->mlinks;
-               if (debug)
-                       say(mlink->file, "Deleting from database");
-               if (nodb)
-                       continue;
-               for ( ; NULL != mlink; mlink = mlink->next) {
-                       i = 1;
-                       SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE],
-                           i, mlink->dsec);
-                       SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE],
-                           i, mlink->arch);
-                       SQL_BIND_TEXT(stmts[STMT_DELETE_PAGE],
-                           i, mlink->name);
-                       SQL_STEP(stmts[STMT_DELETE_PAGE]);
-                       sqlite3_reset(stmts[STMT_DELETE_PAGE]);
+       struct dba_array        *page, *files;
+       char                    *file;
+
+       dba_array_FOREACH(dba->pages, page) {
+               files = dba_array_get(page, DBP_FILE);
+               dba_array_FOREACH(files, file) {
+                       if (*file < ' ')
+                               file++;
+                       if (ohash_find(&mlinks, ohash_qlookup(&mlinks,
+                           file)) != NULL) {
+                               if (debug)
+                                       say(file, "Deleting from database");
+                               dba_array_del(dba->pages);
+                               break;
+                       }
                }
        }
                }
        }
-
-       if (0 == nodb)
-               SQL_EXEC("END TRANSACTION");
 }
 
 /*
 }
 
 /*
- * Close an existing database and its prepared statements.
- * If "real" is not set, rename the temporary file into the real one.
+ * Write the database from memory to disk.
  */
 static void
  */
 static void
-dbclose(int real)
+dbwrite(struct dba *dba)
 {
 {
-       size_t           i;
+       char             tfn[32];
        int              status;
        pid_t            child;
 
        int              status;
        pid_t            child;
 
-       if (nodb)
+       if (dba_write(MANDOC_DB "~", dba) != -1) {
+               if (rename(MANDOC_DB "~", MANDOC_DB) == -1) {
+                       exitcode = (int)MANDOCLEVEL_SYSERR;
+                       say(MANDOC_DB, "&rename");
+                       unlink(MANDOC_DB "~");
+               }
                return;
                return;
-
-       for (i = 0; i < STMT__MAX; i++) {
-               sqlite3_finalize(stmts[i]);
-               stmts[i] = NULL;
        }
 
        }
 
-       sqlite3_close(db);
-       db = NULL;
-
-       if (real)
+       (void)strlcpy(tfn, "/tmp/mandocdb.XXXXXXXX", sizeof(tfn));
+       if (mkdtemp(tfn) == NULL) {
+               exitcode = (int)MANDOCLEVEL_SYSERR;
+               say("", "&%s", tfn);
                return;
                return;
+       }
 
 
-       if ('\0' == *tempfilename) {
-               if (-1 == rename(MANDOC_DB "~", MANDOC_DB)) {
-                       exitcode = (int)MANDOCLEVEL_SYSERR;
-                       say(MANDOC_DB, "&rename");
-               }
-               return;
+       (void)strlcat(tfn, "/" MANDOC_DB, sizeof(tfn));
+       if (dba_write(tfn, dba) == -1) {
+               exitcode = (int)MANDOCLEVEL_SYSERR;
+               say(tfn, "&dba_write");
+               goto out;
        }
 
        switch (child = fork()) {
        }
 
        switch (child = fork()) {
@@ -2233,14 +2103,13 @@ dbclose(int real)
                say("", "&fork cmp");
                return;
        case 0:
                say("", "&fork cmp");
                return;
        case 0:
-               execlp("cmp", "cmp", "-s",
-                   tempfilename, MANDOC_DB, (char *)NULL);
+               execlp("cmp", "cmp", "-s", tfn, MANDOC_DB, (char *)NULL);
                say("", "&exec cmp");
                exit(0);
        default:
                break;
        }
                say("", "&exec cmp");
                exit(0);
        default:
                break;
        }
-       if (-1 == waitpid(child, &status, 0)) {
+       if (waitpid(child, &status, 0) == -1) {
                exitcode = (int)MANDOCLEVEL_SYSERR;
                say("", "&wait cmp");
        } else if (WIFSIGNALED(status)) {
                exitcode = (int)MANDOCLEVEL_SYSERR;
                say("", "&wait cmp");
        } else if (WIFSIGNALED(status)) {
@@ -2252,173 +2121,27 @@ dbclose(int real)
                    "Data changed, but cannot replace database");
        }
 
                    "Data changed, but cannot replace database");
        }
 
-       *strrchr(tempfilename, '/') = '\0';
+out:
+       *strrchr(tfn, '/') = '\0';
        switch (child = fork()) {
        case -1:
                exitcode = (int)MANDOCLEVEL_SYSERR;
                say("", "&fork rm");
                return;
        case 0:
        switch (child = fork()) {
        case -1:
                exitcode = (int)MANDOCLEVEL_SYSERR;
                say("", "&fork rm");
                return;
        case 0:
-               execlp("rm", "rm", "-rf", tempfilename, (char *)NULL);
+               execlp("rm", "rm", "-rf", tfn, (char *)NULL);
                say("", "&exec rm");
                exit((int)MANDOCLEVEL_SYSERR);
        default:
                break;
        }
                say("", "&exec rm");
                exit((int)MANDOCLEVEL_SYSERR);
        default:
                break;
        }
-       if (-1 == waitpid(child, &status, 0)) {
+       if (waitpid(child, &status, 0) == -1) {
                exitcode = (int)MANDOCLEVEL_SYSERR;
                say("", "&wait rm");
        } else if (WIFSIGNALED(status) || WEXITSTATUS(status)) {
                exitcode = (int)MANDOCLEVEL_SYSERR;
                exitcode = (int)MANDOCLEVEL_SYSERR;
                say("", "&wait rm");
        } else if (WIFSIGNALED(status) || WEXITSTATUS(status)) {
                exitcode = (int)MANDOCLEVEL_SYSERR;
-               say("", "%s: Cannot remove temporary directory",
-                   tempfilename);
-       }
-}
-
-/*
- * This is straightforward stuff.
- * Open a database connection to a "temporary" database, then open a set
- * of prepared statements we'll use over and over again.
- * If "real" is set, we use the existing database; if not, we truncate a
- * temporary one.
- * Must be matched by dbclose().
- */
-static int
-dbopen(int real)
-{
-       const char      *sql;
-       int              rc, ofl;
-
-       if (nodb)
-               return 1;
-
-       *tempfilename = '\0';
-       ofl = SQLITE_OPEN_READWRITE;
-
-       if (real) {
-               rc = sqlite3_open_v2(MANDOC_DB, &db, ofl, NULL);
-               if (SQLITE_OK != rc) {
-                       exitcode = (int)MANDOCLEVEL_SYSERR;
-                       if (SQLITE_CANTOPEN != rc)
-                               say(MANDOC_DB, "%s", sqlite3_errstr(rc));
-                       return 0;
-               }
-               goto prepare_statements;
-       }
-
-       ofl |= SQLITE_OPEN_CREATE | SQLITE_OPEN_EXCLUSIVE;
-
-       remove(MANDOC_DB "~");
-       rc = sqlite3_open_v2(MANDOC_DB "~", &db, ofl, NULL);
-       if (SQLITE_OK == rc)
-               goto create_tables;
-       if (MPARSE_QUICK & mparse_options) {
-               exitcode = (int)MANDOCLEVEL_SYSERR;
-               say(MANDOC_DB "~", "%s", sqlite3_errstr(rc));
-               return 0;
-       }
-
-       (void)strlcpy(tempfilename, "/tmp/mandocdb.XXXXXX",
-           sizeof(tempfilename));
-       if (NULL == mkdtemp(tempfilename)) {
-               exitcode = (int)MANDOCLEVEL_SYSERR;
-               say("", "&%s", tempfilename);
-               return 0;
-       }
-       (void)strlcat(tempfilename, "/" MANDOC_DB,
-           sizeof(tempfilename));
-       rc = sqlite3_open_v2(tempfilename, &db, ofl, NULL);
-       if (SQLITE_OK != rc) {
-               exitcode = (int)MANDOCLEVEL_SYSERR;
-               say("", "%s: %s", tempfilename, sqlite3_errstr(rc));
-               return 0;
-       }
-
-create_tables:
-       sql = "CREATE TABLE \"mpages\" (\n"
-             " \"desc\" TEXT NOT NULL,\n"
-             " \"form\" INTEGER NOT NULL,\n"
-             " \"pageid\" INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL\n"
-             ");\n"
-             "\n"
-             "CREATE TABLE \"mlinks\" (\n"
-             " \"sec\" TEXT NOT NULL,\n"
-             " \"arch\" TEXT NOT NULL,\n"
-             " \"name\" TEXT NOT NULL,\n"
-             " \"pageid\" INTEGER NOT NULL REFERENCES mpages(pageid) "
-               "ON DELETE CASCADE\n"
-             ");\n"
-             "CREATE INDEX mlinks_pageid_idx ON mlinks (pageid);\n"
-             "\n"
-             "CREATE TABLE \"names\" (\n"
-             " \"bits\" INTEGER NOT NULL,\n"
-             " \"name\" TEXT NOT NULL,\n"
-             " \"pageid\" INTEGER NOT NULL REFERENCES mpages(pageid) "
-               "ON DELETE CASCADE,\n"
-             " UNIQUE (\"name\", \"pageid\") ON CONFLICT REPLACE\n"
-             ");\n"
-             "\n"
-             "CREATE TABLE \"keys\" (\n"
-             " \"bits\" INTEGER NOT NULL,\n"
-             " \"key\" TEXT NOT NULL,\n"
-             " \"pageid\" INTEGER NOT NULL REFERENCES mpages(pageid) "
-               "ON DELETE CASCADE\n"
-             ");\n"
-             "CREATE INDEX keys_pageid_idx ON keys (pageid);\n";
-
-       if (SQLITE_OK != sqlite3_exec(db, sql, NULL, NULL, NULL)) {
-               exitcode = (int)MANDOCLEVEL_SYSERR;
-               say(MANDOC_DB, "%s", sqlite3_errmsg(db));
-               sqlite3_close(db);
-               return 0;
-       }
-
-prepare_statements:
-       if (SQLITE_OK != sqlite3_exec(db,
-           "PRAGMA foreign_keys = ON", NULL, NULL, NULL)) {
-               exitcode = (int)MANDOCLEVEL_SYSERR;
-               say(MANDOC_DB, "PRAGMA foreign_keys: %s",
-                   sqlite3_errmsg(db));
-               sqlite3_close(db);
-               return 0;
-       }
-
-       sql = "DELETE FROM mpages WHERE pageid IN "
-               "(SELECT pageid FROM mlinks WHERE "
-               "sec=? AND arch=? AND name=?)";
-       sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_DELETE_PAGE], NULL);
-       sql = "INSERT INTO mpages "
-               "(desc,form) VALUES (?,?)";
-       sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_PAGE], NULL);
-       sql = "INSERT INTO mlinks "
-               "(sec,arch,name,pageid) VALUES (?,?,?,?)";
-       sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_LINK], NULL);
-       sql = "SELECT bits FROM names where pageid = ?";
-       sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_SELECT_NAME], NULL);
-       sql = "INSERT INTO names "
-               "(bits,name,pageid) VALUES (?,?,?)";
-       sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_NAME], NULL);
-       sql = "INSERT INTO keys "
-               "(bits,key,pageid) VALUES (?,?,?)";
-       sqlite3_prepare_v2(db, sql, -1, &stmts[STMT_INSERT_KEY], NULL);
-
-#ifndef __APPLE__
-       /*
-        * When opening a new database, we can turn off
-        * synchronous mode for much better performance.
-        */
-
-       if (real && SQLITE_OK != sqlite3_exec(db,
-           "PRAGMA synchronous = OFF", NULL, NULL, NULL)) {
-               exitcode = (int)MANDOCLEVEL_SYSERR;
-               say(MANDOC_DB, "PRAGMA synchronous: %s",
-                   sqlite3_errmsg(db));
-               sqlite3_close(db);
-               return 0;
+               say("", "%s: Cannot remove temporary directory", tfn);
        }
        }
-#endif
-
-       return 1;
 }
 
 static int
 }
 
 static int
index 1ab879d7f2115731e39a2d9aa14afeba7c26aa82..b2410e9973ace63c3be2aa54280ede6b57de2731 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: mansearch.c,v 1.65 2016/07/09 15:24:19 schwarze Exp $ */
+/*     $OpenBSD: mansearch.c,v 1.50 2016/07/09 15:23:36 schwarze Exp $ */
 /*
  * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
 /*
  * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2013, 2014, 2015 Ingo Schwarze <schwarze@openbsd.org>
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
-#include "config.h"
 
 #include <sys/mman.h>
 #include <sys/types.h>
 
 #include <assert.h>
 
 #include <sys/mman.h>
 #include <sys/types.h>
 
 #include <assert.h>
-#if HAVE_ERR
 #include <err.h>
 #include <err.h>
-#endif
 #include <errno.h>
 #include <fcntl.h>
 #include <glob.h>
 #include <errno.h>
 #include <fcntl.h>
 #include <glob.h>
 #include <string.h>
 #include <unistd.h>
 
 #include <string.h>
 #include <unistd.h>
 
-#include <sqlite3.h>
-#ifndef SQLITE_DETERMINISTIC
-#define SQLITE_DETERMINISTIC 0
-#endif
-
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "mandoc_ohash.h"
 #include "manconf.h"
 #include "mansearch.h"
 #include "mandoc.h"
 #include "mandoc_aux.h"
 #include "mandoc_ohash.h"
 #include "manconf.h"
 #include "mansearch.h"
-
-extern int mansearch_keymax;
-extern const char *const mansearch_keynames[];
-
-#define        SQL_BIND_TEXT(_db, _s, _i, _v) \
-       do { if (SQLITE_OK != sqlite3_bind_text \
-               ((_s), (_i)++, (_v), -1, SQLITE_STATIC)) \
-               errx((int)MANDOCLEVEL_SYSERR, "%s", sqlite3_errmsg((_db))); \
-       } while (0)
-#define        SQL_BIND_INT64(_db, _s, _i, _v) \
-       do { if (SQLITE_OK != sqlite3_bind_int64 \
-               ((_s), (_i)++, (_v))) \
-               errx((int)MANDOCLEVEL_SYSERR, "%s", sqlite3_errmsg((_db))); \
-       } while (0)
-#define        SQL_BIND_BLOB(_db, _s, _i, _v) \
-       do { if (SQLITE_OK != sqlite3_bind_blob \
-               ((_s), (_i)++, (&_v), sizeof(_v), SQLITE_STATIC)) \
-               errx((int)MANDOCLEVEL_SYSERR, "%s", sqlite3_errmsg((_db))); \
-       } while (0)
+#include "dbm.h"
 
 struct expr {
 
 struct expr {
-       regex_t          regexp;  /* compiled regexp, if applicable */
-       const char      *substr;  /* to search for, if applicable */
-       struct expr     *next;    /* next in sequence */
-       uint64_t         bits;    /* type-mask */
-       int              equal;   /* equality, not subsring match */
-       int              open;    /* opening parentheses before */
-       int              and;     /* logical AND before */
-       int              close;   /* closing parentheses after */
+       /* Used for terms: */
+       struct dbm_match match;   /* Match type and expression. */
+       uint64_t         bits;    /* Type mask. */
+       /* Used for OR and AND groups: */
+       struct expr     *next;    /* Next child in the parent group. */
+       struct expr     *child;   /* First child in this group. */
+       enum { EXPR_TERM, EXPR_OR, EXPR_AND } type;
 };
 
 };
 
-struct match {
-       uint64_t         pageid; /* identifier in database */
-       uint64_t         bits; /* name type mask */
-       char            *desc; /* manual page description */
-       int              form; /* bit field: formatted, zipped? */
+const char *const mansearch_keynames[KEY_MAX] = {
+       "arch", "sec",  "Xr",   "Ar",   "Fa",   "Fl",   "Dv",   "Fn",
+       "Ic",   "Pa",   "Cm",   "Li",   "Em",   "Cd",   "Va",   "Ft",
+       "Tn",   "Er",   "Ev",   "Sy",   "Sh",   "In",   "Ss",   "Ox",
+       "An",   "Mt",   "St",   "Bx",   "At",   "Nx",   "Fx",   "Lk",
+       "Ms",   "Bsx",  "Dx",   "Rs",   "Vt",   "Lb",   "Nm",   "Nd"
 };
 
 };
 
-static void             buildnames(const struct mansearch *,
-                               struct manpage *, sqlite3 *,
-                               sqlite3_stmt *, uint64_t,
-                               const char *, int form);
-static char            *buildoutput(sqlite3 *, sqlite3_stmt *,
-                                uint64_t, uint64_t);
+
+static struct ohash    *manmerge(struct expr *, struct ohash *);
+static struct ohash    *manmerge_term(struct expr *, struct ohash *);
+static struct ohash    *manmerge_or(struct expr *, struct ohash *);
+static struct ohash    *manmerge_and(struct expr *, struct ohash *);
+static char            *buildnames(const struct dbm_page *);
+static char            *buildoutput(size_t, int32_t);
+static size_t           lstlen(const char *);
+static void             lstcat(char *, size_t *, const char *);
+static int              lstmatch(const char *, const char *);
 static struct expr     *exprcomp(const struct mansearch *,
 static struct expr     *exprcomp(const struct mansearch *,
-                               int, char *[]);
+                               int, char *[], int *);
+static struct expr     *expr_and(const struct mansearch *,
+                               int, char *[], int *);
+static struct expr     *exprterm(const struct mansearch *,
+                               int, char *[], int *);
 static void             exprfree(struct expr *);
 static void             exprfree(struct expr *);
-static struct expr     *exprterm(const struct mansearch *, char *, int);
 static int              manpage_compare(const void *, const void *);
 static int              manpage_compare(const void *, const void *);
-static void             sql_append(char **sql, size_t *sz,
-                               const char *newstr, int count);
-static void             sql_match(sqlite3_context *context,
-                               int argc, sqlite3_value **argv);
-static void             sql_regexp(sqlite3_context *context,
-                               int argc, sqlite3_value **argv);
-static char            *sql_statement(const struct expr *);
-
-
-int
-mansearch_setup(int start)
-{
-       static void     *pagecache;
-       int              c;
-
-#define        PC_PAGESIZE     1280
-#define        PC_NUMPAGES     256
-
-       if (start) {
-               if (NULL != pagecache) {
-                       warnx("pagecache already enabled");
-                       return (int)MANDOCLEVEL_BADARG;
-               }
 
 
-               pagecache = mmap(NULL, PC_PAGESIZE * PC_NUMPAGES,
-                   PROT_READ | PROT_WRITE,
-                   MAP_SHARED | MAP_ANON, -1, 0);
-
-               if (MAP_FAILED == pagecache) {
-                       warn("mmap");
-                       pagecache = NULL;
-                       return (int)MANDOCLEVEL_SYSERR;
-               }
-
-               c = sqlite3_config(SQLITE_CONFIG_PAGECACHE,
-                   pagecache, PC_PAGESIZE, PC_NUMPAGES);
-
-               if (SQLITE_OK == c)
-                       return (int)MANDOCLEVEL_OK;
-
-               warnx("pagecache: %s", sqlite3_errstr(c));
-
-       } else if (NULL == pagecache) {
-               warnx("pagecache missing");
-               return (int)MANDOCLEVEL_BADARG;
-       }
-
-       if (-1 == munmap(pagecache, PC_PAGESIZE * PC_NUMPAGES)) {
-               warn("munmap");
-               pagecache = NULL;
-               return (int)MANDOCLEVEL_SYSERR;
-       }
-
-       pagecache = NULL;
-       return (int)MANDOCLEVEL_OK;
-}
 
 int
 mansearch(const struct mansearch *search,
 
 int
 mansearch(const struct mansearch *search,
@@ -158,21 +84,18 @@ mansearch(const struct mansearch *search,
                int argc, char *argv[],
                struct manpage **res, size_t *sz)
 {
                int argc, char *argv[],
                struct manpage **res, size_t *sz)
 {
-       int64_t          pageid;
-       uint64_t         outbit, iterbit;
        char             buf[PATH_MAX];
        char             buf[PATH_MAX];
-       char            *sql;
+       struct dbm_res  *rp;
+       struct expr     *e;
+       struct dbm_page *page;
        struct manpage  *mpage;
        struct manpage  *mpage;
-       struct expr     *e, *ep;
-       sqlite3         *db;
-       sqlite3_stmt    *s, *s2;
-       struct match    *mp;
-       struct ohash     htab;
-       unsigned int     idx;
-       size_t           i, j, cur, maxres;
-       int              c, chdir_status, getcwd_status, indexbit;
-
-       if (argc == 0 || (e = exprcomp(search, argc, argv)) == NULL) {
+       struct ohash    *htab;
+       size_t           cur, i, maxres, outkey;
+       unsigned int     slot;
+       int              argi, chdir_status, getcwd_status, im;
+
+       argi = 0;
+       if ((e = exprcomp(search, argc, argv, &argi)) == NULL) {
                *sz = 0;
                return 0;
        }
                *sz = 0;
                return 0;
        }
@@ -180,19 +103,14 @@ mansearch(const struct mansearch *search,
        cur = maxres = 0;
        *res = NULL;
 
        cur = maxres = 0;
        *res = NULL;
 
-       if (NULL != search->outkey) {
-               outbit = TYPE_Nd;
-               for (indexbit = 0, iterbit = 1;
-                    indexbit < mansearch_keymax;
-                    indexbit++, iterbit <<= 1) {
+       outkey = KEY_Nd;
+       if (search->outkey != NULL)
+               for (im = 0; im < KEY_MAX; im++)
                        if (0 == strcasecmp(search->outkey,
                        if (0 == strcasecmp(search->outkey,
-                           mansearch_keynames[indexbit])) {
-                               outbit = iterbit;
+                           mansearch_keynames[im])) {
+                               outkey = im;
                                break;
                        }
                                break;
                        }
-               }
-       } else
-               outbit = 0;
 
        /*
         * Remember the original working directory, if possible.
 
        /*
         * Remember the original working directory, if possible.
@@ -208,8 +126,6 @@ mansearch(const struct mansearch *search,
        } else
                getcwd_status = 1;
 
        } else
                getcwd_status = 1;
 
-       sql = sql_statement(e);
-
        /*
         * Loop over the directories (containing databases) for us to
         * search.
        /*
         * Loop over the directories (containing databases) for us to
         * search.
@@ -235,123 +151,48 @@ mansearch(const struct mansearch *search,
                }
                chdir_status = 1;
 
                }
                chdir_status = 1;
 
-               c = sqlite3_open_v2(MANDOC_DB, &db,
-                   SQLITE_OPEN_READONLY, NULL);
-
-               if (SQLITE_OK != c) {
+               if (dbm_open(MANDOC_DB) == -1) {
                        warn("%s/%s", paths->paths[i], MANDOC_DB);
                        warn("%s/%s", paths->paths[i], MANDOC_DB);
-                       sqlite3_close(db);
                        continue;
                }
 
                        continue;
                }
 
-               /*
-                * Define the SQL functions for substring
-                * and regular expression matching.
-                */
-
-               c = sqlite3_create_function(db, "match", 2,
-                   SQLITE_UTF8 | SQLITE_DETERMINISTIC,
-                   NULL, sql_match, NULL, NULL);
-               assert(SQLITE_OK == c);
-               c = sqlite3_create_function(db, "regexp", 2,
-                   SQLITE_UTF8 | SQLITE_DETERMINISTIC,
-                   NULL, sql_regexp, NULL, NULL);
-               assert(SQLITE_OK == c);
-
-               j = 1;
-               c = sqlite3_prepare_v2(db, sql, -1, &s, NULL);
-               if (SQLITE_OK != c)
-                       errx((int)MANDOCLEVEL_SYSERR,
-                           "%s", sqlite3_errmsg(db));
-
-               for (ep = e; NULL != ep; ep = ep->next) {
-                       if (NULL == ep->substr) {
-                               SQL_BIND_BLOB(db, s, j, ep->regexp);
-                       } else
-                               SQL_BIND_TEXT(db, s, j, ep->substr);
-                       if (0 == ((TYPE_Nd | TYPE_Nm) & ep->bits))
-                               SQL_BIND_INT64(db, s, j, ep->bits);
+               if ((htab = manmerge(e, NULL)) == NULL) {
+                       dbm_close();
+                       continue;
                }
 
                }
 
-               mandoc_ohash_init(&htab, 4, offsetof(struct match, pageid));
+               for (rp = ohash_first(htab, &slot); rp != NULL;
+                   rp = ohash_next(htab, &slot)) {
+                       page = dbm_page_get(rp->page);
 
 
-               /*
-                * Hash each entry on its [unique] document identifier.
-                * This is a uint64_t.
-                * Instead of using a hash function, simply convert the
-                * uint64_t to a uint32_t, the hash value's type.
-                * This gives good performance and preserves the
-                * distribution of buckets in the table.
-                */
-               while (SQLITE_ROW == (c = sqlite3_step(s))) {
-                       pageid = sqlite3_column_int64(s, 2);
-                       idx = ohash_lookup_memory(&htab,
-                           (char *)&pageid, sizeof(uint64_t),
-                           (uint32_t)pageid);
-
-                       if (NULL != ohash_find(&htab, idx))
+                       if (lstmatch(search->sec, page->sect) == 0 ||
+                           lstmatch(search->arch, page->arch) == 0)
                                continue;
 
                                continue;
 
-                       mp = mandoc_calloc(1, sizeof(struct match));
-                       mp->pageid = pageid;
-                       mp->form = sqlite3_column_int(s, 1);
-                       mp->bits = sqlite3_column_int64(s, 3);
-                       if (TYPE_Nd == outbit)
-                               mp->desc = mandoc_strdup((const char *)
-                                   sqlite3_column_text(s, 0));
-                       ohash_insert(&htab, idx, mp);
-               }
-
-               if (SQLITE_DONE != c)
-                       warnx("%s", sqlite3_errmsg(db));
-
-               sqlite3_finalize(s);
-
-               c = sqlite3_prepare_v2(db,
-                   "SELECT sec, arch, name, pageid FROM mlinks "
-                   "WHERE pageid=? ORDER BY sec, arch, name",
-                   -1, &s, NULL);
-               if (SQLITE_OK != c)
-                       errx((int)MANDOCLEVEL_SYSERR,
-                           "%s", sqlite3_errmsg(db));
-
-               c = sqlite3_prepare_v2(db,
-                   "SELECT bits, key, pageid FROM keys "
-                   "WHERE pageid=? AND bits & ?",
-                   -1, &s2, NULL);
-               if (SQLITE_OK != c)
-                       errx((int)MANDOCLEVEL_SYSERR,
-                           "%s", sqlite3_errmsg(db));
-
-               for (mp = ohash_first(&htab, &idx);
-                               NULL != mp;
-                               mp = ohash_next(&htab, &idx)) {
                        if (cur + 1 > maxres) {
                                maxres += 1024;
                                *res = mandoc_reallocarray(*res,
                        if (cur + 1 > maxres) {
                                maxres += 1024;
                                *res = mandoc_reallocarray(*res,
-                                   maxres, sizeof(struct manpage));
+                                   maxres, sizeof(**res));
                        }
                        mpage = *res + cur;
                        }
                        mpage = *res + cur;
+                       mandoc_asprintf(&mpage->file, "%s/%s",
+                           paths->paths[i], page->file + 1);
+                       mpage->names = buildnames(page);
+                       mpage->output = (int)outkey == KEY_Nd ?
+                           mandoc_strdup(page->desc) :
+                           buildoutput(outkey, page->addr);
                        mpage->ipath = i;
                        mpage->ipath = i;
-                       mpage->bits = mp->bits;
-                       mpage->sec = 10;
-                       mpage->form = mp->form;
-                       buildnames(search, mpage, db, s, mp->pageid,
-                           paths->paths[i], mp->form);
-                       if (mpage->names != NULL) {
-                               mpage->output = TYPE_Nd & outbit ?
-                                   mp->desc : outbit ?
-                                   buildoutput(db, s2, mp->pageid, outbit) :
-                                   NULL;
-                               cur++;
-                       }
-                       free(mp);
+                       mpage->bits = rp->bits;
+                       mpage->sec = *page->sect - '0';
+                       if (mpage->sec < 0 || mpage->sec > 9)
+                               mpage->sec = 10;
+                       mpage->form = *page->file;
+                       free(rp);
+                       cur++;
                }
                }
-
-               sqlite3_finalize(s);
-               sqlite3_finalize(s2);
-               sqlite3_close(db);
-               ohash_delete(&htab);
+               ohash_delete(htab);
+               free(htab);
+               dbm_close();
 
                /*
                 * In man(1) mode, prefer matches in earlier trees
 
                /*
                 * In man(1) mode, prefer matches in earlier trees
@@ -365,11 +206,169 @@ mansearch(const struct mansearch *search,
        if (chdir_status && getcwd_status && chdir(buf) == -1)
                warn("%s", buf);
        exprfree(e);
        if (chdir_status && getcwd_status && chdir(buf) == -1)
                warn("%s", buf);
        exprfree(e);
-       free(sql);
        *sz = cur;
        return 1;
 }
 
        *sz = cur;
        return 1;
 }
 
+/*
+ * Merge the results for the expression tree rooted at e
+ * into the the result list htab.
+ */
+static struct ohash *
+manmerge(struct expr *e, struct ohash *htab)
+{
+       switch (e->type) {
+       case EXPR_TERM:
+               return manmerge_term(e, htab);
+       case EXPR_OR:
+               return manmerge_or(e->child, htab);
+       case EXPR_AND:
+               return manmerge_and(e->child, htab);
+       default:
+               abort();
+       }
+}
+
+static struct ohash *
+manmerge_term(struct expr *e, struct ohash *htab)
+{
+       struct dbm_res   res, *rp;
+       uint64_t         ib;
+       unsigned int     slot;
+       int              im;
+
+       if (htab == NULL) {
+               htab = mandoc_malloc(sizeof(*htab));
+               mandoc_ohash_init(htab, 4, offsetof(struct dbm_res, page));
+       }
+
+       for (im = 0, ib = 1; im < KEY_MAX; im++, ib <<= 1) {
+               if ((e->bits & ib) == 0)
+                       continue;
+
+               switch (ib) {
+               case TYPE_arch:
+                       dbm_page_byarch(&e->match);
+                       break;
+               case TYPE_sec:
+                       dbm_page_bysect(&e->match);
+                       break;
+               case TYPE_Nm:
+                       dbm_page_byname(&e->match);
+                       break;
+               case TYPE_Nd:
+                       dbm_page_bydesc(&e->match);
+                       break;
+               default:
+                       dbm_page_bymacro(im - 2, &e->match);
+                       break;
+               }
+
+               /*
+                * When hashing for deduplication, use the unique
+                * page ID itself instead of a hash function;
+                * that is quite efficient.
+                */
+
+               for (;;) {
+                       res = dbm_page_next();
+                       if (res.page == -1)
+                               break;
+                       slot = ohash_lookup_memory(htab,
+                           (char *)&res, sizeof(res.page), res.page);
+                       if ((rp = ohash_find(htab, slot)) != NULL) {
+                               rp->bits |= res.bits;
+                               continue;
+                       }
+                       rp = mandoc_malloc(sizeof(*rp));
+                       *rp = res;
+                       ohash_insert(htab, slot, rp);
+               }
+       }
+       return htab;
+}
+
+static struct ohash *
+manmerge_or(struct expr *e, struct ohash *htab)
+{
+       while (e != NULL) {
+               htab = manmerge(e, htab);
+               e = e->next;
+       }
+       return htab;
+}
+
+static struct ohash *
+manmerge_and(struct expr *e, struct ohash *htab)
+{
+       struct ohash    *hand, *h1, *h2;
+       struct dbm_res  *res;
+       unsigned int     slot1, slot2;
+
+       /* Evaluate the first term of the AND clause. */
+
+       hand = manmerge(e, NULL);
+
+       while ((e = e->next) != NULL) {
+
+               /* Evaluate the next term and prepare for ANDing. */
+
+               h2 = manmerge(e, NULL);
+               if (ohash_entries(h2) < ohash_entries(hand)) {
+                       h1 = h2;
+                       h2 = hand;
+               } else
+                       h1 = hand;
+               hand = mandoc_malloc(sizeof(*hand));
+               mandoc_ohash_init(hand, 4, offsetof(struct dbm_res, page));
+
+               /* Keep all pages that are in both result sets. */
+
+               for (res = ohash_first(h1, &slot1); res != NULL;
+                   res = ohash_next(h1, &slot1)) {
+                       if (ohash_find(h2, ohash_lookup_memory(h2,
+                           (char *)res, sizeof(res->page),
+                           res->page)) == NULL)
+                               free(res);
+                       else
+                               ohash_insert(hand, ohash_lookup_memory(hand,
+                                   (char *)res, sizeof(res->page),
+                                   res->page), res);
+               }
+
+               /* Discard the merged results. */
+
+               for (res = ohash_first(h2, &slot2); res != NULL;
+                   res = ohash_next(h2, &slot2))
+                       free(res);
+               ohash_delete(h2);
+               free(h2);
+               ohash_delete(h1);
+               free(h1);
+       }
+
+       /* Merge the result of the AND into htab. */
+
+       if (htab == NULL)
+               return hand;
+
+       for (res = ohash_first(hand, &slot1); res != NULL;
+           res = ohash_next(hand, &slot1)) {
+               slot2 = ohash_lookup_memory(htab,
+                   (char *)res, sizeof(res->page), res->page);
+               if (ohash_find(htab, slot2) == NULL)
+                       ohash_insert(htab, slot2, res);
+               else
+                       free(res);
+       }
+
+       /* Discard the merged result. */
+
+       ohash_delete(hand);
+       free(hand);
+       return htab;
+}
+
 void
 mansearch_free(struct manpage *res, size_t sz)
 {
 void
 mansearch_free(struct manpage *res, size_t sz)
 {
@@ -396,260 +395,114 @@ manpage_compare(const void *vp1, const void *vp2)
            strcasecmp(mp1->names, mp2->names);
 }
 
            strcasecmp(mp1->names, mp2->names);
 }
 
-static void
-buildnames(const struct mansearch *search, struct manpage *mpage,
-               sqlite3 *db, sqlite3_stmt *s,
-               uint64_t pageid, const char *path, int form)
-{
-       glob_t           globinfo;
-       char            *firstname, *newnames, *prevsec, *prevarch;
-       const char      *oldnames, *sep1, *name, *sec, *sep2, *arch, *fsec;
-       size_t           i;
-       int              c, globres;
-
-       mpage->file = NULL;
-       mpage->names = NULL;
-       firstname = prevsec = prevarch = NULL;
-       i = 1;
-       SQL_BIND_INT64(db, s, i, pageid);
-       while (SQLITE_ROW == (c = sqlite3_step(s))) {
-
-               /* Decide whether we already have some names. */
-
-               if (NULL == mpage->names) {
-                       oldnames = "";
-                       sep1 = "";
-               } else {
-                       oldnames = mpage->names;
-                       sep1 = ", ";
-               }
-
-               /* Fetch the next name, rejecting sec/arch mismatches. */
-
-               sec = (const char *)sqlite3_column_text(s, 0);
-               if (search->sec != NULL && strcasecmp(sec, search->sec))
-                       continue;
-               arch = (const char *)sqlite3_column_text(s, 1);
-               if (search->arch != NULL && *arch != '\0' &&
-                   strcasecmp(arch, search->arch))
-                       continue;
-               name = (const char *)sqlite3_column_text(s, 2);
-
-               /* Remember the first section found. */
-
-               if (9 < mpage->sec && '1' <= *sec && '9' >= *sec)
-                       mpage->sec = (*sec - '1') + 1;
-
-               /* If the section changed, append the old one. */
-
-               if (NULL != prevsec &&
-                   (strcmp(sec, prevsec) ||
-                    strcmp(arch, prevarch))) {
-                       sep2 = '\0' == *prevarch ? "" : "/";
-                       mandoc_asprintf(&newnames, "%s(%s%s%s)",
-                           oldnames, prevsec, sep2, prevarch);
-                       free(mpage->names);
-                       oldnames = mpage->names = newnames;
-                       free(prevsec);
-                       free(prevarch);
-                       prevsec = prevarch = NULL;
-               }
-
-               /* Save the new section, to append it later. */
-
-               if (NULL == prevsec) {
-                       prevsec = mandoc_strdup(sec);
-                       prevarch = mandoc_strdup(arch);
-               }
-
-               /* Append the new name. */
-
-               mandoc_asprintf(&newnames, "%s%s%s",
-                   oldnames, sep1, name);
-               free(mpage->names);
-               mpage->names = newnames;
-
-               /* Also save the first file name encountered. */
-
-               if (mpage->file != NULL)
-                       continue;
-
-               if (form & FORM_SRC) {
-                       sep1 = "man";
-                       fsec = sec;
-               } else {
-                       sep1 = "cat";
-                       fsec = "0";
-               }
-               sep2 = *arch == '\0' ? "" : "/";
-               mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.%s",
-                   path, sep1, sec, sep2, arch, name, fsec);
-               if (access(mpage->file, R_OK) != -1)
-                       continue;
-
-               /* Handle unusual file name extensions. */
-
-               if (firstname == NULL)
-                       firstname = mpage->file;
-               else
-                       free(mpage->file);
-               mandoc_asprintf(&mpage->file, "%s/%s%s%s%s/%s.*",
-                   path, sep1, sec, sep2, arch, name);
-               globres = glob(mpage->file, 0, NULL, &globinfo);
-               free(mpage->file);
-               mpage->file = globres ? NULL :
-                   mandoc_strdup(*globinfo.gl_pathv);
-               globfree(&globinfo);
-       }
-       if (c != SQLITE_DONE)
-               warnx("%s", sqlite3_errmsg(db));
-       sqlite3_reset(s);
-
-       /* If none of the files is usable, use the first name. */
-
-       if (mpage->file == NULL)
-               mpage->file = firstname;
-       else if (mpage->file != firstname)
-               free(firstname);
-
-       /* Append one final section to the names. */
-
-       if (prevsec != NULL) {
-               sep2 = *prevarch == '\0' ? "" : "/";
-               mandoc_asprintf(&newnames, "%s(%s%s%s)",
-                   mpage->names, prevsec, sep2, prevarch);
-               free(mpage->names);
-               mpage->names = newnames;
-               free(prevsec);
-               free(prevarch);
-       }
-}
-
 static char *
 static char *
-buildoutput(sqlite3 *db, sqlite3_stmt *s, uint64_t pageid, uint64_t outbit)
+buildnames(const struct dbm_page *page)
 {
 {
-       char            *output, *newoutput;
-       const char      *oldoutput, *sep1, *data;
-       size_t           i;
-       int              c;
-
-       output = NULL;
-       i = 1;
-       SQL_BIND_INT64(db, s, i, pageid);
-       SQL_BIND_INT64(db, s, i, outbit);
-       while (SQLITE_ROW == (c = sqlite3_step(s))) {
-               if (NULL == output) {
-                       oldoutput = "";
-                       sep1 = "";
-               } else {
-                       oldoutput = output;
-                       sep1 = " # ";
-               }
-               data = (const char *)sqlite3_column_text(s, 1);
-               mandoc_asprintf(&newoutput, "%s%s%s",
-                   oldoutput, sep1, data);
-               free(output);
-               output = newoutput;
+       char    *buf;
+       size_t   i, sz;
+
+       sz = lstlen(page->name) + 1 + lstlen(page->sect) +
+           (page->arch == NULL ? 0 : 1 + lstlen(page->arch)) + 2;
+       buf = mandoc_malloc(sz);
+       i = 0;
+       lstcat(buf, &i, page->name);
+       buf[i++] = '(';
+       lstcat(buf, &i, page->sect);
+       if (page->arch != NULL) {
+               buf[i++] = '/';
+               lstcat(buf, &i, page->arch);
        }
        }
-       if (SQLITE_DONE != c)
-               warnx("%s", sqlite3_errmsg(db));
-       sqlite3_reset(s);
-       return output;
+       buf[i++] = ')';
+       buf[i++] = '\0';
+       assert(i == sz);
+       return buf;
 }
 
 /*
 }
 
 /*
- * Implement substring match as an application-defined SQL function.
- * Using the SQL LIKE or GLOB operators instead would be a bad idea
- * because that would require escaping metacharacters in the string
- * being searched for.
+ * Count the buffer space needed to print the NUL-terminated
+ * list of NUL-terminated strings, when printing two separator
+ * characters between strings.
  */
  */
-static void
-sql_match(sqlite3_context *context, int argc, sqlite3_value **argv)
+static size_t
+lstlen(const char *cp)
 {
 {
+       size_t   sz;
 
 
-       assert(2 == argc);
-       sqlite3_result_int(context, NULL != strcasestr(
-           (const char *)sqlite3_value_text(argv[1]),
-           (const char *)sqlite3_value_text(argv[0])));
+       for (sz = 0;; sz++) {
+               if (cp[0] == '\0') {
+                       if (cp[1] == '\0')
+                               break;
+                       sz++;
+               } else if (cp[0] < ' ')
+                       sz--;
+               cp++;
+       }
+       return sz;
 }
 
 /*
 }
 
 /*
- * Implement regular expression match
- * as an application-defined SQL function.
+ * Print the NUL-terminated list of NUL-terminated strings
+ * into the buffer, seperating strings with a comma and a blank.
  */
 static void
  */
 static void
-sql_regexp(sqlite3_context *context, int argc, sqlite3_value **argv)
+lstcat(char *buf, size_t *i, const char *cp)
 {
 {
-
-       assert(2 == argc);
-       sqlite3_result_int(context, !regexec(
-           (regex_t *)sqlite3_value_blob(argv[0]),
-           (const char *)sqlite3_value_text(argv[1]),
-           0, NULL, 0));
+       for (;;) {
+               if (cp[0] == '\0') {
+                       if (cp[1] == '\0')
+                               break;
+                       buf[(*i)++] = ',';
+                       buf[(*i)++] = ' ';
+               } else if (cp[0] >= ' ')
+                       buf[(*i)++] = cp[0];
+               cp++;
+       }
 }
 
 }
 
-static void
-sql_append(char **sql, size_t *sz, const char *newstr, int count)
+/*
+ * Return 1 if the string *want occurs in any of the strings
+ * in the NUL-terminated string list *have, or 0 otherwise.
+ * If either argument is NULL or empty, assume no filtering
+ * is desired and return 1.
+ */
+static int
+lstmatch(const char *want, const char *have)
 {
 {
-       size_t           newsz;
-
-       newsz = 1 < count ? (size_t)count : strlen(newstr);
-       *sql = mandoc_realloc(*sql, *sz + newsz + 1);
-       if (1 < count)
-               memset(*sql + *sz, *newstr, (size_t)count);
-       else
-               memcpy(*sql + *sz, newstr, newsz);
-       *sz += newsz;
-       (*sql)[*sz] = '\0';
+        if (want == NULL || have == NULL || *have == '\0')
+                return 1;
+        while (*have != '\0') {
+                if (strcasestr(have, want) != NULL)
+                        return 1;
+                have = strchr(have, '\0') + 1;
+        }
+        return 0;
 }
 
 /*
 }
 
 /*
- * Prepare the search SQL statement.
+ * Build a list of values taken by the macro im
+ * in the manual page with big-endian address addr.
  */
 static char *
  */
 static char *
-sql_statement(const struct expr *e)
+buildoutput(size_t im, int32_t addr)
 {
 {
-       char            *sql;
-       size_t           sz;
-       int              needop;
-
-       sql = mandoc_strdup(e->equal ?
-           "SELECT desc, form, pageid, bits "
-               "FROM mpages NATURAL JOIN names WHERE " :
-           "SELECT desc, form, pageid, 0 FROM mpages WHERE ");
-       sz = strlen(sql);
-
-       for (needop = 0; NULL != e; e = e->next) {
-               if (e->and)
-                       sql_append(&sql, &sz, " AND ", 1);
-               else if (needop)
-                       sql_append(&sql, &sz, " OR ", 1);
-               if (e->open)
-                       sql_append(&sql, &sz, "(", e->open);
-               sql_append(&sql, &sz,
-                   TYPE_Nd & e->bits
-                   ? (NULL == e->substr
-                       ? "desc REGEXP ?"
-                       : "desc MATCH ?")
-                   : TYPE_Nm == e->bits
-                   ? (NULL == e->substr
-                       ? "pageid IN (SELECT pageid FROM names "
-                         "WHERE name REGEXP ?)"
-                       : e->equal
-                       ? "name = ? "
-                       : "pageid IN (SELECT pageid FROM names "
-                         "WHERE name MATCH ?)")
-                   : (NULL == e->substr
-                       ? "pageid IN (SELECT pageid FROM keys "
-                         "WHERE key REGEXP ? AND bits & ?)"
-                       : "pageid IN (SELECT pageid FROM keys "
-                         "WHERE key MATCH ? AND bits & ?)"), 1);
-               if (e->close)
-                       sql_append(&sql, &sz, ")", e->close);
-               needop = 1;
-       }
+       const char      *oldoutput, *sep;
+       char            *output, *newoutput, *value;
 
 
-       return sql;
+       output = NULL;
+       dbm_macro_bypage(im - 2, addr);
+       while ((value = dbm_macro_next()) != NULL) {
+               if (output == NULL) {
+                       oldoutput = "";
+                       sep = "";
+               } else {
+                       oldoutput = output;
+                       sep = " # ";
+               }
+               mandoc_asprintf(&newoutput, "%s%s%s", oldoutput, sep, value);
+               free(output);
+               output = newoutput;
+       }
+       return output;
 }
 
 /*
 }
 
 /*
@@ -658,195 +511,238 @@ sql_statement(const struct expr *e)
  * "(", "foo=bar", etc.).
  */
 static struct expr *
  * "(", "foo=bar", etc.).
  */
 static struct expr *
-exprcomp(const struct mansearch *search, int argc, char *argv[])
+exprcomp(const struct mansearch *search, int argc, char *argv[], int *argi)
 {
 {
-       uint64_t         mask;
-       int              i, toopen, logic, igncase, toclose;
-       struct expr     *first, *prev, *cur, *next;
-
-       first = cur = NULL;
-       logic = igncase = toopen = toclose = 0;
-
-       for (i = 0; i < argc; i++) {
-               if (0 == strcmp("(", argv[i])) {
-                       if (igncase)
-                               goto fail;
-                       toopen++;
-                       toclose++;
-                       continue;
-               } else if (0 == strcmp(")", argv[i])) {
-                       if (toopen || logic || igncase || NULL == cur)
-                               goto fail;
-                       cur->close++;
-                       if (0 > --toclose)
-                               goto fail;
-                       continue;
-               } else if (0 == strcmp("-a", argv[i])) {
-                       if (toopen || logic || igncase || NULL == cur)
-                               goto fail;
-                       logic = 1;
+       struct expr     *parent, *child;
+       int              needterm, nested;
+
+       if ((nested = *argi) == argc)
+               return NULL;
+       needterm = 1;
+       parent = child = NULL;
+       while (*argi < argc) {
+               if (strcmp(")", argv[*argi]) == 0) {
+                       if (needterm)
+                               warnx("missing term "
+                                   "before closing parenthesis");
+                       needterm = 0;
+                       if (nested)
+                               break;
+                       warnx("ignoring unmatched right parenthesis");
+                       ++*argi;
                        continue;
                        continue;
-               } else if (0 == strcmp("-o", argv[i])) {
-                       if (toopen || logic || igncase || NULL == cur)
-                               goto fail;
-                       logic = 2;
+               }
+               if (strcmp("-o", argv[*argi]) == 0) {
+                       if (needterm) {
+                               if (*argi > 0)
+                                       warnx("ignoring -o after %s",
+                                           argv[*argi - 1]);
+                               else
+                                       warnx("ignoring initial -o");
+                       }
+                       needterm = 1;
+                       ++*argi;
                        continue;
                        continue;
-               } else if (0 == strcmp("-i", argv[i])) {
-                       if (igncase)
-                               goto fail;
-                       igncase = 1;
+               }
+               needterm = 0;
+               if (child == NULL) {
+                       child = expr_and(search, argc, argv, argi);
                        continue;
                }
                        continue;
                }
-               next = exprterm(search, argv[i], !igncase);
-               if (NULL == next)
-                       goto fail;
-               if (NULL == first)
-                       first = next;
-               else
-                       cur->next = next;
-               prev = cur = next;
-
-               /*
-                * Searching for descriptions must be split out
-                * because they are stored in the mpages table,
-                * not in the keys table.
-                */
+               if (parent == NULL) {
+                       parent = mandoc_calloc(1, sizeof(*parent));
+                       parent->type = EXPR_OR;
+                       parent->next = NULL;
+                       parent->child = child;
+               }
+               child->next = expr_and(search, argc, argv, argi);
+               child = child->next;
+       }
+       if (needterm && *argi)
+               warnx("ignoring trailing %s", argv[*argi - 1]);
+       return parent == NULL ? child : parent;
+}
 
 
-               for (mask = TYPE_Nm; mask <= TYPE_Nd; mask <<= 1) {
-                       if (mask & cur->bits && ~mask & cur->bits) {
-                               next = mandoc_calloc(1,
-                                   sizeof(struct expr));
-                               memcpy(next, cur, sizeof(struct expr));
-                               prev->open = 1;
-                               cur->bits = mask;
-                               cur->next = next;
-                               cur = next;
-                               cur->bits &= ~mask;
+static struct expr *
+expr_and(const struct mansearch *search, int argc, char *argv[], int *argi)
+{
+       struct expr     *parent, *child;
+       int              needterm;
+
+       needterm = 1;
+       parent = child = NULL;
+       while (*argi < argc) {
+               if (strcmp(")", argv[*argi]) == 0) {
+                       if (needterm)
+                               warnx("missing term "
+                                   "before closing parenthesis");
+                       needterm = 0;
+                       break;
+               }
+               if (strcmp("-o", argv[*argi]) == 0)
+                       break;
+               if (strcmp("-a", argv[*argi]) == 0) {
+                       if (needterm) {
+                               if (*argi > 0)
+                                       warnx("ignoring -a after %s",
+                                           argv[*argi - 1]);
+                               else
+                                       warnx("ignoring initial -a");
                        }
                        }
+                       needterm = 1;
+                       ++*argi;
+                       continue;
+               }
+               if (needterm == 0)
+                       break;
+               if (child == NULL) {
+                       child = exprterm(search, argc, argv, argi);
+                       if (child != NULL)
+                               needterm = 0;
+                       continue;
+               }
+               needterm = 0;
+               if (parent == NULL) {
+                       parent = mandoc_calloc(1, sizeof(*parent));
+                       parent->type = EXPR_AND;
+                       parent->next = NULL;
+                       parent->child = child;
+               }
+               child->next = exprterm(search, argc, argv, argi);
+               if (child->next != NULL) {
+                       child = child->next;
+                       needterm = 0;
                }
                }
-               prev->and = (1 == logic);
-               prev->open += toopen;
-               if (cur != prev)
-                       cur->close = 1;
-
-               toopen = logic = igncase = 0;
        }
        }
-       if ( ! (toopen || logic || igncase || toclose))
-               return first;
-
-fail:
-       if (NULL != first)
-               exprfree(first);
-       return NULL;
+       if (needterm && *argi)
+               warnx("ignoring trailing %s", argv[*argi - 1]);
+       return parent == NULL ? child : parent;
 }
 
 static struct expr *
 }
 
 static struct expr *
-exprterm(const struct mansearch *search, char *buf, int cs)
+exprterm(const struct mansearch *search, int argc, char *argv[], int *argi)
 {
        char             errbuf[BUFSIZ];
        struct expr     *e;
        char            *key, *val;
        uint64_t         iterbit;
 {
        char             errbuf[BUFSIZ];
        struct expr     *e;
        char            *key, *val;
        uint64_t         iterbit;
-       int              i, irc;
-
-       if ('\0' == *buf)
-               return NULL;
+       int              cs, i, irc;
+
+       if (strcmp("(", argv[*argi]) == 0) {
+               ++*argi;
+               e = exprcomp(search, argc, argv, argi);
+               if (*argi < argc) {
+                       assert(strcmp(")", argv[*argi]) == 0);
+                       ++*argi;
+               } else
+                       warnx("unclosed parenthesis");
+               return e;
+       }
 
 
-       e = mandoc_calloc(1, sizeof(struct expr));
+       e = mandoc_calloc(1, sizeof(*e));
+       e->type = EXPR_TERM;
+       e->bits = 0;
+       e->next = NULL;
+       e->child = NULL;
 
        if (search->argmode == ARG_NAME) {
                e->bits = TYPE_Nm;
 
        if (search->argmode == ARG_NAME) {
                e->bits = TYPE_Nm;
-               e->substr = buf;
-               e->equal = 1;
+               e->match.type = DBM_EXACT;
+               e->match.str = argv[(*argi)++];
                return e;
        }
 
        /*
         * Separate macro keys from search string.
                return e;
        }
 
        /*
         * Separate macro keys from search string.
-        * If needed, request regular expression handling
-        * by setting e->substr to NULL.
+        * If needed, request regular expression handling.
         */
 
        if (search->argmode == ARG_WORD) {
                e->bits = TYPE_Nm;
         */
 
        if (search->argmode == ARG_WORD) {
                e->bits = TYPE_Nm;
-               e->substr = NULL;
+               e->match.type = DBM_REGEX;
 #if HAVE_REWB_BSD
 #if HAVE_REWB_BSD
-               mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf);
+               mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", argv[*argi]);
 #elif HAVE_REWB_SYSV
 #elif HAVE_REWB_SYSV
-               mandoc_asprintf(&val, "\\<%s\\>", buf);
+               mandoc_asprintf(&val, "\\<%s\\>", argv[*argi]);
 #else
                mandoc_asprintf(&val,
 #else
                mandoc_asprintf(&val,
-                   "(^|[^a-zA-Z01-9_])%s([^a-zA-Z01-9_]|$)", buf);
+                   "(^|[^a-zA-Z01-9_])%s([^a-zA-Z01-9_]|$)", argv[*argi]);
 #endif
                cs = 0;
 #endif
                cs = 0;
-       } else if ((val = strpbrk(buf, "=~")) == NULL) {
+       } else if ((val = strpbrk(argv[*argi], "=~")) == NULL) {
                e->bits = TYPE_Nm | TYPE_Nd;
                e->bits = TYPE_Nm | TYPE_Nd;
-               e->substr = buf;
+               e->match.type = DBM_SUB;
+               e->match.str = argv[*argi];
        } else {
        } else {
-               if (val == buf)
+               if (val == argv[*argi])
                        e->bits = TYPE_Nm | TYPE_Nd;
                        e->bits = TYPE_Nm | TYPE_Nd;
-               if ('=' == *val)
-                       e->substr = val + 1;
+               if (*val == '=') {
+                       e->match.type = DBM_SUB;
+                       e->match.str = val + 1;
+               } else
+                       e->match.type = DBM_REGEX;
                *val++ = '\0';
                *val++ = '\0';
-               if (NULL != strstr(buf, "arch"))
+               if (strstr(argv[*argi], "arch") != NULL)
                        cs = 0;
        }
 
        /* Compile regular expressions. */
 
                        cs = 0;
        }
 
        /* Compile regular expressions. */
 
-       if (NULL == e->substr) {
-               irc = regcomp(&e->regexp, val,
+       if (e->match.type == DBM_REGEX) {
+               e->match.re = mandoc_malloc(sizeof(*e->match.re));
+               irc = regcomp(e->match.re, val,
                    REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE));
                    REG_EXTENDED | REG_NOSUB | (cs ? 0 : REG_ICASE));
+               if (irc) {
+                       regerror(irc, e->match.re, errbuf, sizeof(errbuf));
+                       warnx("regcomp /%s/: %s", val, errbuf);
+               }
                if (search->argmode == ARG_WORD)
                        free(val);
                if (irc) {
                if (search->argmode == ARG_WORD)
                        free(val);
                if (irc) {
-                       regerror(irc, &e->regexp, errbuf, sizeof(errbuf));
-                       warnx("regcomp: %s", errbuf);
+                       free(e->match.re);
                        free(e);
                        free(e);
+                       ++*argi;
                        return NULL;
                }
        }
 
                        return NULL;
                }
        }
 
-       if (e->bits)
+       if (e->bits) {
+               ++*argi;
                return e;
                return e;
+       }
 
        /*
         * Parse out all possible fields.
         * If the field doesn't resolve, bail.
         */
 
 
        /*
         * Parse out all possible fields.
         * If the field doesn't resolve, bail.
         */
 
-       while (NULL != (key = strsep(&buf, ","))) {
+       while (NULL != (key = strsep(&argv[*argi], ","))) {
                if ('\0' == *key)
                        continue;
                if ('\0' == *key)
                        continue;
-               for (i = 0, iterbit = 1;
-                    i < mansearch_keymax;
-                    i++, iterbit <<= 1) {
-                       if (0 == strcasecmp(key,
-                           mansearch_keynames[i])) {
+               for (i = 0, iterbit = 1; i < KEY_MAX; i++, iterbit <<= 1) {
+                       if (0 == strcasecmp(key, mansearch_keynames[i])) {
                                e->bits |= iterbit;
                                break;
                        }
                }
                                e->bits |= iterbit;
                                break;
                        }
                }
-               if (i == mansearch_keymax) {
-                       if (strcasecmp(key, "any")) {
-                               free(e);
-                               return NULL;
-                       }
+               if (i == KEY_MAX) {
+                       if (strcasecmp(key, "any"))
+                               warnx("treating unknown key "
+                                   "\"%s\" as \"any\"", key);
                        e->bits |= ~0ULL;
                }
        }
 
                        e->bits |= ~0ULL;
                }
        }
 
+       ++*argi;
        return e;
 }
 
 static void
        return e;
 }
 
 static void
-exprfree(struct expr *p)
+exprfree(struct expr *e)
 {
 {
-       struct expr     *pp;
-
-       while (NULL != p) {
-               pp = p->next;
-               free(p);
-               p = pp;
-       }
+       if (e->next != NULL)
+               exprfree(e->next);
+       if (e->child != NULL)
+               exprfree(e->child);
+       free(e);
 }
 }
index 7f68ff676750373cc928cb8e5b8c4912c2ae6e17..acad65da2fb3c0f1bce7d5611413d9ed36ee1cce 100644 (file)
@@ -1,4 +1,4 @@
-/*     $Id: mansearch.h,v 1.24 2015/11/07 14:01:16 schwarze Exp $ */
+/*     $Id: mansearch.h,v 1.25 2016/07/19 21:31:55 schwarze Exp $ */
 /*
  * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
 /*
  * Copyright (c) 2012 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2013, 2014 Ingo Schwarze <schwarze@openbsd.org>
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  */
 
-#define        MANDOC_DB        "mandoc.db"
+#define        MANDOC_DB        "mandoc.new.db"
+#define        MANDOCDB_MAGIC   0x3a7d0cdb
+#define        MANDOCDB_VERSION 0  /* XXX Start counting in production. */
+
+#define        MACRO_MAX        36
+#define        KEY_Nd           39
+#define        KEY_MAX          40
 
 #define        TYPE_arch        0x0000000000000001ULL
 #define        TYPE_sec         0x0000000000000002ULL
 
 #define        TYPE_arch        0x0000000000000001ULL
 #define        TYPE_sec         0x0000000000000002ULL
 #define        NAME_FILE        0x0000004000000010ULL
 #define        NAME_MASK        0x000000000000001fULL
 
 #define        NAME_FILE        0x0000004000000010ULL
 #define        NAME_MASK        0x000000000000001fULL
 
-#define        FORM_CAT         0  /* manual page is preformatted */
-#define        FORM_SRC         1  /* format is mdoc(7) or man(7) */
-#define        FORM_NONE        4  /* format is unknown */
+enum   form {
+       FORM_SRC = 1,   /* Format is mdoc(7) or man(7). */
+       FORM_CAT,       /* Manual page is preformatted. */
+       FORM_NONE       /* Format is unknown. */
+};
 
 enum   argmode {
        ARG_FILE = 0,
 
 enum   argmode {
        ARG_FILE = 0,
@@ -84,7 +92,7 @@ struct        manpage {
        size_t           ipath; /* number of the manpath */
        uint64_t         bits; /* name type mask */
        int              sec; /* section number, 10 means invalid */
        size_t           ipath; /* number of the manpath */
        uint64_t         bits; /* name type mask */
        int              sec; /* section number, 10 means invalid */
-       int              form; /* 0 == catpage */
+       enum form        form;
 };
 
 struct mansearch {
 };
 
 struct mansearch {
@@ -98,7 +106,6 @@ struct       mansearch {
 
 struct manpaths;
 
 
 struct manpaths;
 
-int    mansearch_setup(int);
 int    mansearch(const struct mansearch *cfg, /* options */
                const struct manpaths *paths, /* manpaths */
                int argc, /* size of argv */
 int    mansearch(const struct mansearch *cfg, /* options */
                const struct manpaths *paths, /* manpaths */
                int argc, /* size of argv */
diff --git a/read.c b/read.c
index e516cdaffc6f006d958a92697b67aeeb17f0f675..21d28b0bc08f430e9a79966d420bcebf90cae9d4 100644 (file)
--- a/read.c
+++ b/read.c
@@ -1,4 +1,4 @@
-/*     $Id: read.c,v 1.150 2016/07/19 16:22:52 schwarze Exp $ */
+/*     $Id: read.c,v 1.151 2016/07/19 21:31:55 schwarze Exp $ */
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2016 Ingo Schwarze <schwarze@openbsd.org>
 /*
  * Copyright (c) 2008, 2009, 2010, 2011 Kristaps Dzonsons <kristaps@bsd.lv>
  * Copyright (c) 2010-2016 Ingo Schwarze <schwarze@openbsd.org>
 #include "config.h"
 
 #include <sys/types.h>
 #include "config.h"
 
 #include <sys/types.h>
-#if HAVE_MMAP
 #include <sys/mman.h>
 #include <sys/stat.h>
 #include <sys/mman.h>
 #include <sys/stat.h>
-#endif
 
 #include <assert.h>
 #include <ctype.h>
 
 #include <assert.h>
 #include <ctype.h>
@@ -598,7 +596,6 @@ read_whole_file(struct mparse *curp, const char *file, int fd,
        size_t           off;
        ssize_t          ssz;
 
        size_t           off;
        ssize_t          ssz;
 
-#if HAVE_MMAP
        struct stat      st;
 
        if (fstat(fd, &st) == -1)
        struct stat      st;
 
        if (fstat(fd, &st) == -1)
@@ -622,7 +619,6 @@ read_whole_file(struct mparse *curp, const char *file, int fd,
                if (fb->buf != MAP_FAILED)
                        return 1;
        }
                if (fb->buf != MAP_FAILED)
                        return 1;
        }
-#endif
 
        if (curp->gzip) {
                if ((gz = gzdopen(fd, "rb")) == NULL)
 
        if (curp->gzip) {
                if ((gz = gzdopen(fd, "rb")) == NULL)
@@ -747,11 +743,9 @@ mparse_readfd(struct mparse *curp, int fd, const char *file)
                    (MPARSE_UTF8 | MPARSE_LATIN1);
                mparse_parse_buffer(curp, blk, file);
                curp->filenc = save_filenc;
                    (MPARSE_UTF8 | MPARSE_LATIN1);
                mparse_parse_buffer(curp, blk, file);
                curp->filenc = save_filenc;
-#if HAVE_MMAP
                if (with_mmap)
                        munmap(blk.buf, blk.sz);
                else
                if (with_mmap)
                        munmap(blk.buf, blk.sz);
                else
-#endif
                        free(blk.buf);
        }
        return curp->file_status;
                        free(blk.buf);
        }
        return curp->file_status;
diff --git a/test-mmap.c b/test-mmap.c
deleted file mode 100644 (file)
index 3a6232d..0000000
+++ /dev/null
@@ -1,9 +0,0 @@
-#include <sys/types.h>
-#include <sys/mman.h>
-#include <stddef.h>
-
-int
-main(void)
-{
-       return mmap(NULL, 1, PROT_READ, MAP_SHARED, -1, 0) != MAP_FAILED;
-}
diff --git a/test-sqlite3.c b/test-sqlite3.c
deleted file mode 100644 (file)
index 11f17ad..0000000
+++ /dev/null
@@ -1,47 +0,0 @@
-/*     $Id: test-sqlite3.c,v 1.2 2015/10/06 18:32:20 schwarze Exp $    */
-/*
- * Copyright (c) 2014 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.
- */
-
-#include <stdio.h>
-#include <unistd.h>
-#include <sqlite3.h>
-
-int
-main(void)
-{
-       sqlite3 *db;
-
-       if (sqlite3_open_v2("test.db", &db,
-           SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE,
-           NULL) != SQLITE_OK) {
-               perror("test.db");
-               fprintf(stderr, "sqlite3_open_v2: %s", sqlite3_errmsg(db));
-               return 1;
-       }
-       unlink("test.db");
-
-       if (sqlite3_exec(db, "PRAGMA foreign_keys = ON",
-           NULL, NULL, NULL) != SQLITE_OK) {
-               fprintf(stderr, "sqlite3_exec: %s", sqlite3_errmsg(db));
-               return 1;
-       }
-
-       if (sqlite3_close(db) != SQLITE_OK) {
-               fprintf(stderr, "sqlite3_close: %s", sqlite3_errmsg(db));
-               return 1;
-       }
-       return 0;
-}
diff --git a/test-sqlite3_errstr.c b/test-sqlite3_errstr.c
deleted file mode 100644 (file)
index 4d3c7c5..0000000
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <string.h>
-#include <sqlite3.h>
-
-int
-main(void)
-{
-       return strcmp(sqlite3_errstr(SQLITE_OK), "not an error");
-}