]> 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
 ----------------------------------------------
@@ -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.
 
-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
@@ -84,20 +84,9 @@ manual page source.
 
 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
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>
@@ -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.
 
-VERSION = 1.13.4
+VERSION = 1.14.0
 
 # === LIST OF FILES ====================================================
 
@@ -26,7 +26,6 @@ TESTSRCS       = test-dirent-namlen.c \
                   test-getsubopt.c \
                   test-isblank.c \
                   test-mkdtemp.c \
-                  test-mmap.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-sqlite3.c \
-                  test-sqlite3_errstr.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_sqlite3_errstr.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 \
+                  dba.c \
+                  dba_array.c \
+                  dba_read.c \
+                  dba_write.c \
+                  dbm.c \
+                  dbm_map.c \
                   demandoc.c \
                   eqn.c \
                   eqn_html.c \
@@ -86,7 +88,6 @@ SRCS           = att.c \
                   manpage.c \
                   manpath.c \
                   mansearch.c \
-                  mansearch_const.c \
                   mdoc.c \
                   mdoc_argv.c \
                   mdoc_hash.c \
@@ -128,6 +129,11 @@ DISTFILES   = INSTALL \
                   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 \
@@ -220,7 +226,6 @@ COMPAT_OBJS  = compat_err.o \
                   compat_ohash.o \
                   compat_progname.o \
                   compat_reallocarray.o \
-                  compat_sqlite3_errstr.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
 
-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) \
+                  $(DBM_OBJS) \
+                  $(DBA_OBJS) \
                   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) \
+                  $(DBM_OBJS) \
                   cgi.o \
-                  mansearch.o \
-                  mansearch_const.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
 
@@ -329,7 +341,7 @@ www: $(WWW_OBJS) $(WWW_MANS)
 
 $(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
@@ -341,7 +353,7 @@ distclean: clean
 
 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)
@@ -351,47 +363,40 @@ clean:
 
 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)$(MANDIR)/man8
        $(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_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 \
-               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) 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
-
-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
 
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
-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_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_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_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
@@ -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
+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
@@ -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
-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
-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
@@ -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
-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
diff --git a/TODO b/TODO
index 99a16a9e62a4575eff1f6e42d69950491aa1d58a..c22d1978b039749b62a152ddefc32cbdc1f92fa4 100644 (file)
--- a/TODO
+++ b/TODO
@@ -1,6 +1,6 @@
 ************************************************************************
 * 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:
@@ -612,7 +612,6 @@ are mere guesses, and some may be wrong.
 ************************************************************************
 
 - 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
@@ -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
 
-- 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
 ************************************************************************
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=
-LD_SQLITE3=
 STATIC="-static"
 
-BUILD_DB=1
 BUILD_CGI=0
 
 HAVE_DIRENT_NAMLEN=
@@ -53,7 +51,6 @@ HAVE_GETLINE=
 HAVE_GETSUBOPT=
 HAVE_ISBLANK=
 HAVE_MKDTEMP=
-HAVE_MMAP=
 HAVE_PLEDGE=
 HAVE_PROGNAME=
 HAVE_REALLOCARRAY=
@@ -70,8 +67,6 @@ HAVE_STRTONUM=
 HAVE_VASPRINTF=
 HAVE_WCHAR=
 
-HAVE_SQLITE3=
-HAVE_SQLITE3_ERRSTR=
 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 mmap           MMAP            || 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
 
-# --- 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
        :
@@ -256,7 +212,7 @@ if [ "${HAVE_OHASH}" -eq 0 ]; then
 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
@@ -315,7 +271,6 @@ cat << __HEREDOC__
 #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}
@@ -331,8 +286,6 @@ cat << __HEREDOC__
 #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}
 
@@ -371,9 +324,6 @@ fi
 [ ${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 *);"
 
@@ -413,17 +363,10 @@ exec > Makefile.local
 [ -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}
@@ -460,9 +403,6 @@ INSTALL_MAN = ${INSTALL_MAN}
 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
 
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>
 #
@@ -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:
 
-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,
@@ -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"
 
-# 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.
-# 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_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
@@ -129,6 +135,13 @@ BINM_SOELIM=msoelim                # default is "soelim"
 
 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).
@@ -150,43 +163,6 @@ INSTALL_LIB="${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,
@@ -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.
-# Obviously, this requires that BUILD_DB is enabled, too.
 
 BUILD_CGI=1
 
@@ -258,7 +233,7 @@ HAVE_GETLINE=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
@@ -273,7 +248,3 @@ HAVE_STRSEP=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
  * 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>
@@ -82,9 +82,7 @@ struct        curparse {
 };
 
 
-#if HAVE_SQLITE3
 int                      mandocdb(int, char *[]);
-#endif
 
 static int               fs_lookup(const struct manpaths *,
                                size_t ipath, const char *,
@@ -147,11 +145,9 @@ main(int argc, char *argv[])
        setprogname(progname);
 #endif
 
-#if HAVE_SQLITE3
        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)
@@ -349,9 +345,6 @@ main(int argc, char *argv[])
        /* 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;
@@ -359,19 +352,9 @@ main(int argc, char *argv[])
                /* 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);
-#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)
@@ -474,7 +457,7 @@ main(int argc, char *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);
@@ -522,10 +505,7 @@ main(int argc, char *argv[])
 out:
        if (search.argmode != ARG_FILE) {
                manconf_free(&conf);
-#if HAVE_SQLITE3
                mansearch_free(res, sz);
-               mansearch_setup(0);
-#endif
        }
 
        free(defos);
@@ -629,7 +609,8 @@ fs_lookup(const struct manpaths *paths, size_t ipath,
        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",
@@ -667,10 +648,8 @@ fs_lookup(const struct manpaths *paths, size_t ipath,
                return 0;
 
 found:
-#if HAVE_SQLITE3
        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;
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>
@@ -37,6 +37,7 @@
 #if HAVE_SANDBOX_INIT
 #include <sandbox.h>
 #endif
+#include <stdarg.h>
 #include <stddef.h>
 #include <stdio.h>
 #include <stdint.h>
@@ -44,8 +45,6 @@
 #include <string.h>
 #include <unistd.h>
 
-#include <sqlite3.h>
-
 #include "mandoc_aux.h"
 #include "mandoc_ohash.h"
 #include "mandoc.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[];
 
-#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 */
@@ -98,14 +79,14 @@ struct      inodev {
 
 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 */
-       int              form;    /* format from file content */
        int              name_head_done;
+       enum form        form;    /* format from file content */
 };
 
 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 */
-       int              dform;   /* format from directory */
-       int              fform;   /* format from file name 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 *,
@@ -142,20 +113,17 @@ struct    mdoc_handler {
 
 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_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     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 *);
@@ -191,7 +159,6 @@ static      int      set_basedir(const char *, int);
 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 */
@@ -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 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] = {
@@ -341,6 +306,7 @@ mandocdb(int argc, char *argv[])
 {
        struct manconf    conf;
        struct mparse    *mp;
+       struct dba       *dba;
        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));
-       memset(stmts, 0, STMT__MAX * sizeof(sqlite3_stmt *));
 
        /*
         * 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 (dbopen(1)) {
+               if ((dba = dba_read(MANDOC_DB)) != NULL) {
                        /*
                         * 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)
-                               dbprune();
+                               dbprune(dba);
                } else {
                        /*
                         * Database missing or corrupt.
@@ -489,12 +454,13 @@ mandocdb(int argc, char *argv[])
                        op = OP_DEFAULT;
                        if (0 == treescan())
                                goto out;
-                       if (0 == dbopen(0))
-                               goto out;
+                       dba = dba_new(128);
                }
                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.
@@ -539,14 +505,11 @@ mandocdb(int argc, char *argv[])
                                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();
@@ -596,7 +559,8 @@ treescan(void)
        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];
@@ -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->form = FORM_NONE;
                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
-mpages_merge(struct mparse *mp)
+mpages_merge(struct dba *dba, struct mparse *mp)
 {
        char                     any[] = "any";
        struct mpage            *mpage, *mpage_dest;
@@ -1129,9 +1094,6 @@ mpages_merge(struct mparse *mp)
        int                      fd;
        unsigned int             pslot;
 
-       if ( ! nodb)
-               SQL_EXEC("BEGIN TRANSACTION");
-
        mpage = ohash_first(&mpages, &pslot);
        while (mpage != NULL) {
                mlinks_undupe(mpage);
@@ -1188,8 +1150,8 @@ mpages_merge(struct mparse *mp)
                                         * to the target.
                                         */
 
-                                       if (mpage_dest->pageid)
-                                               dbadd_mlink_name(mlink);
+                                       if (mpage_dest->dba != NULL)
+                                               dbadd_mlink(mlink);
 
                                        if (mlink->next == NULL)
                                                break;
@@ -1254,7 +1216,7 @@ mpages_merge(struct mparse *mp)
                             mlink = mlink->next)
                                mlink_check(mpage, mlink);
 
-               dbadd(mpage);
+               dbadd(dba, mpage);
                mlink = mpage->mlinks;
 
 nextpage:
@@ -1262,44 +1224,6 @@ nextpage:
                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
@@ -1823,7 +1747,7 @@ putkeys(const struct mpage *mpage, char *cp, size_t sz, uint64_t v)
        } 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",
@@ -2029,53 +1953,23 @@ render_string(char **public, size_t *psz)
 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.
- * 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
-dbadd(struct mpage *mpage)
+dbadd(struct dba *dba, struct mpage *mpage)
 {
        struct mlink    *mlink;
        struct str      *key;
        char            *cp;
+       uint64_t         mask;
        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);
-       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);
 
-       while (NULL != mlink) {
+       while ((mlink = mlink->next) != NULL)
                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);
-               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);
-               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
-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
-dbclose(int real)
+dbwrite(struct dba *dba)
 {
-       size_t           i;
+       char             tfn[32];
        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;
-
-       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;
+       }
 
-       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()) {
@@ -2233,14 +2103,13 @@ dbclose(int real)
                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;
        }
-       if (-1 == waitpid(child, &status, 0)) {
+       if (waitpid(child, &status, 0) == -1) {
                exitcode = (int)MANDOCLEVEL_SYSERR;
                say("", "&wait cmp");
        } else if (WIFSIGNALED(status)) {
@@ -2252,173 +2121,27 @@ dbclose(int real)
                    "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:
-               execlp("rm", "rm", "-rf", tempfilename, (char *)NULL);
+               execlp("rm", "rm", "-rf", tfn, (char *)NULL);
                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;
-               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
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>
  * 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>
-#if HAVE_ERR
 #include <err.h>
-#endif
 #include <errno.h>
 #include <fcntl.h>
 #include <glob.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"
-
-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 {
-       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 *,
-                               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 struct expr     *exprterm(const struct mansearch *, char *, int);
 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,
@@ -158,21 +84,18 @@ mansearch(const struct mansearch *search,
                int argc, char *argv[],
                struct manpage **res, size_t *sz)
 {
-       int64_t          pageid;
-       uint64_t         outbit, iterbit;
        char             buf[PATH_MAX];
-       char            *sql;
+       struct dbm_res  *rp;
+       struct expr     *e;
+       struct dbm_page *page;
        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;
        }
@@ -180,19 +103,14 @@ mansearch(const struct mansearch *search,
        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,
-                           mansearch_keynames[indexbit])) {
-                               outbit = iterbit;
+                           mansearch_keynames[im])) {
+                               outkey = im;
                                break;
                        }
-               }
-       } else
-               outbit = 0;
 
        /*
         * Remember the original working directory, if possible.
@@ -208,8 +126,6 @@ mansearch(const struct mansearch *search,
        } else
                getcwd_status = 1;
 
-       sql = sql_statement(e);
-
        /*
         * Loop over the directories (containing databases) for us to
         * search.
@@ -235,123 +151,48 @@ mansearch(const struct mansearch *search,
                }
                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);
-                       sqlite3_close(db);
                        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;
 
-                       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,
-                                   maxres, sizeof(struct manpage));
+                                   maxres, sizeof(**res));
                        }
                        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->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
@@ -365,11 +206,169 @@ mansearch(const struct mansearch *search,
        if (chdir_status && getcwd_status && chdir(buf) == -1)
                warn("%s", buf);
        exprfree(e);
-       free(sql);
        *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)
 {
@@ -396,260 +395,114 @@ manpage_compare(const void *vp1, const void *vp2)
            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 *
-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
-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 *
-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 *
-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;
-               } 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;
-               } 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;
                }
-               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 *
-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;
-       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;
-               e->substr = buf;
-               e->equal = 1;
+               e->match.type = DBM_EXACT;
+               e->match.str = argv[(*argi)++];
                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;
-               e->substr = NULL;
+               e->match.type = DBM_REGEX;
 #if HAVE_REWB_BSD
-               mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", buf);
+               mandoc_asprintf(&val, "[[:<:]]%s[[:>:]]", argv[*argi]);
 #elif HAVE_REWB_SYSV
-               mandoc_asprintf(&val, "\\<%s\\>", buf);
+               mandoc_asprintf(&val, "\\<%s\\>", argv[*argi]);
 #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;
-       } else if ((val = strpbrk(buf, "=~")) == NULL) {
+       } else if ((val = strpbrk(argv[*argi], "=~")) == NULL) {
                e->bits = TYPE_Nm | TYPE_Nd;
-               e->substr = buf;
+               e->match.type = DBM_SUB;
+               e->match.str = argv[*argi];
        } else {
-               if (val == buf)
+               if (val == argv[*argi])
                        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';
-               if (NULL != strstr(buf, "arch"))
+               if (strstr(argv[*argi], "arch") != NULL)
                        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));
+               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) {
-                       regerror(irc, &e->regexp, errbuf, sizeof(errbuf));
-                       warnx("regcomp: %s", errbuf);
+                       free(e->match.re);
                        free(e);
+                       ++*argi;
                        return NULL;
                }
        }
 
-       if (e->bits)
+       if (e->bits) {
+               ++*argi;
                return e;
+       }
 
        /*
         * 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;
-               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;
                        }
                }
-               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;
                }
        }
 
+       ++*argi;
        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>
  * 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        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,
@@ -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 */
-       int              form; /* 0 == catpage */
+       enum form        form;
 };
 
 struct mansearch {
@@ -98,7 +106,6 @@ struct       mansearch {
 
 struct manpaths;
 
-int    mansearch_setup(int);
 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>
 #include "config.h"
 
 #include <sys/types.h>
-#if HAVE_MMAP
 #include <sys/mman.h>
 #include <sys/stat.h>
-#endif
 
 #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;
 
-#if HAVE_MMAP
        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;
        }
-#endif
 
        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;
-#if HAVE_MMAP
                if (with_mmap)
                        munmap(blk.buf, blk.sz);
                else
-#endif
                        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");
-}