summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorIngo Schwarze <schwarze@openbsd.org>2016-07-19 21:31:55 +0000
committerIngo Schwarze <schwarze@openbsd.org>2016-07-19 21:31:55 +0000
commit4fe58b8b37f6b19d2c50441b4df20ca5687fe6ed (patch)
tree99ff72d0dd74a36a5e20f8cf1225a78361b8b4af
parent0512b80d5f59824b3d18cd1494119fd904528ca5 (diff)
downloadmandoc-4fe58b8b37f6b19d2c50441b4df20ca5687fe6ed.tar.gz
Remove the dependency on SQLite without loss of functionality.
Stop supporting systems that don't have mmap(3). Drop the obsolete names_check() now that we deleted MLINKS.
-rw-r--r--INSTALL19
-rw-r--r--Makefile79
-rw-r--r--Makefile.depend20
-rw-r--r--TODO6
-rw-r--r--compat_sqlite3_errstr.c16
-rwxr-xr-xconfigure70
-rw-r--r--configure.local.example65
-rw-r--r--dba.c418
-rw-r--r--dba.h51
-rw-r--r--dba_array.c188
-rw-r--r--dba_array.h47
-rw-r--r--dba_read.c74
-rw-r--r--dba_write.c117
-rw-r--r--dba_write.h (renamed from test-sqlite3.c)47
-rw-r--r--dbm.c452
-rw-r--r--dbm.h68
-rw-r--r--dbm_map.c174
-rw-r--r--dbm_map.h (renamed from mansearch_const.c)28
-rw-r--r--main.c27
-rw-r--r--mandocdb.c459
-rw-r--r--mansearch.c1046
-rw-r--r--mansearch.h19
-rw-r--r--read.c6
-rw-r--r--test-mmap.c9
-rw-r--r--test-sqlite3_errstr.c8
25 files changed, 2275 insertions, 1238 deletions
diff --git a/INSTALL b/INSTALL
index 5d0ff4f9..94990b9d 100644
--- a/INSTALL
+++ b/INSTALL
@@ -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
diff --git a/Makefile b/Makefile
index 4ac7172f..44fed4fb 100644
--- a/Makefile
+++ b/Makefile
@@ -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
diff --git a/Makefile.depend b/Makefile.depend
index d2d2e001..e0c3a2e9 100644
--- a/Makefile.depend
+++ b/Makefile.depend
@@ -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 b64ac0f1..032b1803 100644
--- a/TODO
+++ b/TODO
@@ -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
index 8a6ace28..00000000
--- a/compat_sqlite3_errstr.c
+++ /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
diff --git a/configure b/configure
index 9e7128e6..77993f7d 100755
--- 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
diff --git a/configure.local.example b/configure.local.example
index 8304ded8..146ca726 100644
--- a/configure.local.example
+++ b/configure.local.example
@@ -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
index 00000000..9eabbb0a
--- /dev/null
+++ b/dba.c
@@ -0,0 +1,418 @@
+/* $Id$ */
+/*
+ * 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
index 00000000..885c987a
--- /dev/null
+++ b/dba.h
@@ -0,0 +1,51 @@
+/* $Id$ */
+/*
+ * 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
index 00000000..da5fc2e9
--- /dev/null
+++ b/dba_array.c
@@ -0,0 +1,188 @@
+/* $Id$ */
+/*
+ * 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
index 00000000..d9ad2665
--- /dev/null
+++ b/dba_array.h
@@ -0,0 +1,47 @@
+/* $Id$ */
+/*
+ * 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
index 00000000..f36c553c
--- /dev/null
+++ b/dba_read.c
@@ -0,0 +1,74 @@
+/* $Id$ */
+/*
+ * 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
index 00000000..9746d575
--- /dev/null
+++ b/dba_write.c
@@ -0,0 +1,117 @@
+/* $Id$ */
+/*
+ * 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/test-sqlite3.c b/dba_write.h
index 0ec76744..4346af26 100644
--- a/test-sqlite3.c
+++ b/dba_write.h
@@ -1,6 +1,6 @@
-/* $Id$ */
+/* $Id$ */
/*
- * 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
@@ -13,35 +13,18 @@
* 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.
*/
-#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;
-}
+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
index 00000000..d10417b2
--- /dev/null
+++ b/dbm.c
@@ -0,0 +1,452 @@
+/* $Id$ */
+/*
+ * 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
index 00000000..171d0910
--- /dev/null
+++ b/dbm.h
@@ -0,0 +1,68 @@
+/* $Id$ */
+/*
+ * 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
index 00000000..8d24832d
--- /dev/null
+++ b/dbm_map.c
@@ -0,0 +1,174 @@
+/* $Id$ */
+/*
+ * 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();
+ }
+}
diff --git a/mansearch_const.c b/dbm_map.h
index 54768024..41690a6c 100644
--- a/mansearch_const.c
+++ b/dbm_map.h
@@ -1,6 +1,6 @@
/* $Id$ */
/*
- * 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
@@ -13,21 +13,17 @@
* 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 40c06b8f..12f4c995 100644
--- a/main.c
+++ b/main.c
@@ -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;
diff --git a/mandocdb.c b/mandocdb.c
index e08d855a..a3247bec 100644
--- a/mandocdb.c
+++ b/mandocdb.c
@@ -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"
@@ -54,29 +53,11 @@
#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
diff --git a/mansearch.c b/mansearch.c
index 66cd7796..b2410e99 100644
--- a/mansearch.c
+++ b/mansearch.c
@@ -1,4 +1,4 @@
-/* $Id$ */
+/* $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>
@@ -15,15 +15,12 @@
* 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>
@@ -36,121 +33,50 @@
#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);
}
diff --git a/mansearch.h b/mansearch.h
index c6171f7b..82076e23 100644
--- a/mansearch.h
+++ b/mansearch.h
@@ -16,7 +16,13 @@
* 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
@@ -66,9 +72,11 @@
#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 f6c78bd7..ea7670c0 100644
--- a/read.c
+++ b/read.c
@@ -19,10 +19,8 @@
#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
index 3a6232d9..00000000
--- a/test-mmap.c
+++ /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_errstr.c b/test-sqlite3_errstr.c
deleted file mode 100644
index 4d3c7c54..00000000
--- a/test-sqlite3_errstr.c
+++ /dev/null
@@ -1,8 +0,0 @@
-#include <string.h>
-#include <sqlite3.h>
-
-int
-main(void)
-{
- return strcmp(sqlite3_errstr(SQLITE_OK), "not an error");
-}