1 /* $Id: dbm.c,v 1.7 2019/07/01 22:56:24 schwarze Exp $ */
3 * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * Map-based version of the mandoc database, for read-only access.
18 * The interface is defined in "dbm.h".
26 #include <sys/endian.h>
28 #include <arpa/inet.h>
40 #include "mansearch.h"
66 static struct macro *macros[MACRO_MAX];
67 static int32_t nvals[MACRO_MAX];
68 static struct page *pages;
69 static int32_t npages;
70 static enum iter iteration;
72 static struct dbm_res page_bytitle(enum iter, const struct dbm_match *);
73 static struct dbm_res page_byarch(const struct dbm_match *);
74 static struct dbm_res page_bymacro(int32_t, const struct dbm_match *);
75 static char *macro_bypage(int32_t, int32_t);
78 /*** top level functions **********************************************/
81 * Open a disk-based mandoc database for read-only access.
82 * Map the pages and macros[] arrays.
83 * Return 0 on success. Return -1 and set errno on failure.
86 dbm_open(const char *fname)
88 const int32_t *mp, *ep;
91 if (dbm_map(fname) == -1)
94 if ((npages = be32toh(*dbm_getint(4))) < 0) {
95 warnx("dbm_open(%s): Invalid number of pages: %d",
99 pages = (struct page *)dbm_getint(5);
101 if ((mp = dbm_get(*dbm_getint(2))) == NULL) {
102 warnx("dbm_open(%s): Invalid offset of macros array", fname);
105 if (be32toh(*mp) != MACRO_MAX) {
106 warnx("dbm_open(%s): Invalid number of macros: %d",
107 fname, be32toh(*mp));
110 for (im = 0; im < MACRO_MAX; im++) {
111 if ((ep = dbm_get(*++mp)) == NULL) {
112 warnx("dbm_open(%s): Invalid offset of macro %d",
116 nvals[im] = be32toh(*ep);
117 macros[im] = (struct macro *)++ep;
134 /*** functions for handling pages *************************************/
143 * Give the caller pointers to the data for one manual page.
146 dbm_page_get(int32_t ip)
148 static struct dbm_page res;
152 res.name = dbm_get(pages[ip].name);
153 if (res.name == NULL)
154 res.name = "(NULL)\0";
155 res.sect = dbm_get(pages[ip].sect);
156 if (res.sect == NULL)
157 res.sect = "(NULL)\0";
158 res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
159 res.desc = dbm_get(pages[ip].desc);
160 if (res.desc == NULL)
162 res.file = dbm_get(pages[ip].file);
163 if (res.file == NULL)
164 res.file = " (NULL)\0";
165 res.addr = dbm_addr(pages + ip);
170 * Functions to start filtered iterations over manual pages.
173 dbm_page_byname(const struct dbm_match *match)
175 assert(match != NULL);
176 page_bytitle(ITER_NAME, match);
180 dbm_page_bysect(const struct dbm_match *match)
182 assert(match != NULL);
183 page_bytitle(ITER_SECT, match);
187 dbm_page_byarch(const struct dbm_match *match)
189 assert(match != NULL);
194 dbm_page_bydesc(const struct dbm_match *match)
196 assert(match != NULL);
197 page_bytitle(ITER_DESC, match);
201 dbm_page_bymacro(int32_t im, const struct dbm_match *match)
204 assert(im < MACRO_MAX);
205 assert(match != NULL);
206 page_bymacro(im, match);
210 * Return the number of the next manual page in the current iteration.
215 struct dbm_res res = {-1, 0};
221 return page_byarch(NULL);
223 return page_bymacro(0, NULL);
225 return page_bytitle(iteration, NULL);
230 * Functions implementing the iteration over manual pages.
232 static struct dbm_res
233 page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match)
235 static const struct dbm_match *match;
236 static const char *cp;
238 struct dbm_res res = {-1, 0};
240 assert(arg_iter == ITER_NAME || arg_iter == ITER_DESC ||
241 arg_iter == ITER_SECT);
243 /* Initialize for a new iteration. */
245 if (arg_match != NULL) {
246 iteration = arg_iter;
250 cp = dbm_get(pages[0].name);
253 cp = dbm_get(pages[0].sect);
256 cp = dbm_get(pages[0].desc);
262 iteration = ITER_NONE;
271 /* Search for a name. */
273 while (ip < npages) {
274 if (iteration == ITER_NAME)
276 if (dbm_match(match, cp))
278 cp = strchr(cp, '\0') + 1;
279 if (iteration == ITER_DESC)
281 else if (*cp == '\0') {
287 /* Reached the end without a match. */
290 iteration = ITER_NONE;
296 /* Found a match; save the quality for later retrieval. */
299 res.bits = iteration == ITER_NAME ? cp[-1] : 0;
301 /* Skip the remaining names of this page. */
306 } while (cp[-1] != '\0' ||
307 (iteration != ITER_DESC && cp[-2] != '\0'));
312 static struct dbm_res
313 page_byarch(const struct dbm_match *arg_match)
315 static const struct dbm_match *match;
316 struct dbm_res res = {-1, 0};
320 /* Initialize for a new iteration. */
322 if (arg_match != NULL) {
323 iteration = ITER_ARCH;
329 /* Search for an architecture. */
331 for ( ; ip < npages; ip++)
333 for (cp = dbm_get(pages[ip].arch);
335 cp = strchr(cp, '\0') + 1)
336 if (dbm_match(match, cp)) {
341 /* Reached the end without a match. */
343 iteration = ITER_NONE;
348 static struct dbm_res
349 page_bymacro(int32_t arg_im, const struct dbm_match *arg_match)
351 static const struct dbm_match *match;
352 static const int32_t *pp;
353 static const char *cp;
354 static int32_t im, iv;
355 struct dbm_res res = {-1, 0};
358 assert(im < MACRO_MAX);
360 /* Initialize for a new iteration. */
362 if (arg_match != NULL) {
363 iteration = ITER_MACRO;
366 cp = nvals[im] ? dbm_get(macros[im]->value) : NULL;
371 if (iteration != ITER_MACRO)
374 /* Find the next matching macro value. */
376 while (pp == NULL || *pp == 0) {
377 if (++iv == nvals[im]) {
378 iteration = ITER_NONE;
382 cp = strchr(cp, '\0') + 1;
383 if (dbm_match(match, cp))
384 pp = dbm_get(macros[im][iv].pages);
387 /* Found a matching page. */
389 res.page = (struct page *)dbm_get(*pp++) - pages;
394 /*** functions for handling macros ************************************/
397 dbm_macro_count(int32_t im)
400 assert(im < MACRO_MAX);
405 dbm_macro_get(int32_t im, int32_t iv)
407 static struct dbm_macro macro;
410 assert(im < MACRO_MAX);
412 assert(iv < nvals[im]);
413 macro.value = dbm_get(macros[im][iv].value);
414 macro.pp = dbm_get(macros[im][iv].pages);
419 * Filtered iteration over macro entries.
422 dbm_macro_bypage(int32_t im, int32_t ip)
425 assert(im < MACRO_MAX);
427 macro_bypage(im, ip);
433 return macro_bypage(MACRO_MAX, 0);
437 macro_bypage(int32_t arg_im, int32_t arg_ip)
439 static const int32_t *pp;
440 static int32_t im, ip, iv;
442 /* Initialize for a new iteration. */
444 if (arg_im < MACRO_MAX && arg_ip != 0) {
447 pp = dbm_get(macros[im]->pages);
454 /* Search for the next value. */
456 while (iv < nvals[im]) {
464 /* Reached the end without a match. */
466 if (iv == nvals[im]) {
473 /* Found a match; skip the remaining pages of this entry. */
475 if (++iv < nvals[im])
479 return dbm_get(macros[im][iv - 1].value);