Annotation of mandoc/dbm.c, Revision 1.1
1.1 ! schwarze 1: /* $Id$ */
! 2: /*
! 3: * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
! 4: *
! 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.
! 8: *
! 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.
! 16: *
! 17: * Map-based version of the mandoc database, for read-only access.
! 18: * The interface is defined in "dbm.h".
! 19: */
! 20: #include <assert.h>
! 21: #include <endian.h>
! 22: #include <err.h>
! 23: #include <errno.h>
! 24: #include <regex.h>
! 25: #include <stdint.h>
! 26: #include <stdio.h>
! 27: #include <stdlib.h>
! 28: #include <string.h>
! 29:
! 30: #include "mansearch.h"
! 31: #include "dbm_map.h"
! 32: #include "dbm.h"
! 33:
! 34: struct macro {
! 35: int32_t value;
! 36: int32_t pages;
! 37: };
! 38:
! 39: struct page {
! 40: int32_t name;
! 41: int32_t sect;
! 42: int32_t arch;
! 43: int32_t desc;
! 44: int32_t file;
! 45: };
! 46:
! 47: enum iter {
! 48: ITER_NONE = 0,
! 49: ITER_NAME,
! 50: ITER_SECT,
! 51: ITER_ARCH,
! 52: ITER_DESC,
! 53: ITER_MACRO
! 54: };
! 55:
! 56: static struct macro *macros[MACRO_MAX];
! 57: static int32_t nvals[MACRO_MAX];
! 58: static struct page *pages;
! 59: static int32_t npages;
! 60: static enum iter iteration;
! 61:
! 62: static struct dbm_res page_bytitle(enum iter, const struct dbm_match *);
! 63: static struct dbm_res page_byarch(const struct dbm_match *);
! 64: static struct dbm_res page_bymacro(int32_t, const struct dbm_match *);
! 65: static char *macro_bypage(int32_t, int32_t);
! 66:
! 67:
! 68: /*** top level functions **********************************************/
! 69:
! 70: /*
! 71: * Open a disk-based mandoc database for read-only access.
! 72: * Map the pages and macros[] arrays.
! 73: * Return 0 on success. Return -1 and set errno on failure.
! 74: */
! 75: int
! 76: dbm_open(const char *fname)
! 77: {
! 78: const int32_t *mp, *ep;
! 79: int32_t im;
! 80:
! 81: if (dbm_map(fname) == -1)
! 82: return -1;
! 83:
! 84: if ((npages = be32toh(*dbm_getint(4))) < 0) {
! 85: warnx("dbm_open(%s): Invalid number of pages: %d",
! 86: fname, npages);
! 87: goto fail;
! 88: }
! 89: pages = (struct page *)dbm_getint(5);
! 90:
! 91: if ((mp = dbm_get(*dbm_getint(2))) == NULL) {
! 92: warnx("dbm_open(%s): Invalid offset of macros array", fname);
! 93: goto fail;
! 94: }
! 95: if (be32toh(*mp) != MACRO_MAX) {
! 96: warnx("dbm_open(%s): Invalid number of macros: %d",
! 97: fname, be32toh(*mp));
! 98: goto fail;
! 99: }
! 100: for (im = 0; im < MACRO_MAX; im++) {
! 101: if ((ep = dbm_get(*++mp)) == NULL) {
! 102: warnx("dbm_open(%s): Invalid offset of macro %d",
! 103: fname, im);
! 104: goto fail;
! 105: }
! 106: nvals[im] = be32toh(*ep);
! 107: macros[im] = (struct macro *)++ep;
! 108: }
! 109: return 0;
! 110:
! 111: fail:
! 112: dbm_unmap();
! 113: errno = EFTYPE;
! 114: return -1;
! 115: }
! 116:
! 117: void
! 118: dbm_close(void)
! 119: {
! 120: dbm_unmap();
! 121: }
! 122:
! 123:
! 124: /*** functions for handling pages *************************************/
! 125:
! 126: int32_t
! 127: dbm_page_count(void)
! 128: {
! 129: return npages;
! 130: }
! 131:
! 132: /*
! 133: * Give the caller pointers to the data for one manual page.
! 134: */
! 135: struct dbm_page *
! 136: dbm_page_get(int32_t ip)
! 137: {
! 138: static struct dbm_page res;
! 139:
! 140: assert(ip >= 0);
! 141: assert(ip < npages);
! 142: res.name = dbm_get(pages[ip].name);
! 143: res.sect = dbm_get(pages[ip].sect);
! 144: res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
! 145: res.desc = dbm_get(pages[ip].desc);
! 146: res.file = dbm_get(pages[ip].file);
! 147: res.addr = dbm_addr(pages + ip);
! 148: return &res;
! 149: }
! 150:
! 151: /*
! 152: * Functions to start filtered iterations over manual pages.
! 153: */
! 154: void
! 155: dbm_page_byname(const struct dbm_match *match)
! 156: {
! 157: assert(match != NULL);
! 158: page_bytitle(ITER_NAME, match);
! 159: }
! 160:
! 161: void
! 162: dbm_page_bysect(const struct dbm_match *match)
! 163: {
! 164: assert(match != NULL);
! 165: page_bytitle(ITER_SECT, match);
! 166: }
! 167:
! 168: void
! 169: dbm_page_byarch(const struct dbm_match *match)
! 170: {
! 171: assert(match != NULL);
! 172: page_byarch(match);
! 173: }
! 174:
! 175: void
! 176: dbm_page_bydesc(const struct dbm_match *match)
! 177: {
! 178: assert(match != NULL);
! 179: page_bytitle(ITER_DESC, match);
! 180: }
! 181:
! 182: void
! 183: dbm_page_bymacro(int32_t im, const struct dbm_match *match)
! 184: {
! 185: assert(im >= 0);
! 186: assert(im < MACRO_MAX);
! 187: assert(match != NULL);
! 188: page_bymacro(im, match);
! 189: }
! 190:
! 191: /*
! 192: * Return the number of the next manual page in the current iteration.
! 193: */
! 194: struct dbm_res
! 195: dbm_page_next(void)
! 196: {
! 197: struct dbm_res res = {-1, 0};
! 198:
! 199: switch(iteration) {
! 200: case ITER_NONE:
! 201: return res;
! 202: case ITER_ARCH:
! 203: return page_byarch(NULL);
! 204: case ITER_MACRO:
! 205: return page_bymacro(0, NULL);
! 206: default:
! 207: return page_bytitle(iteration, NULL);
! 208: }
! 209: }
! 210:
! 211: /*
! 212: * Functions implementing the iteration over manual pages.
! 213: */
! 214: static struct dbm_res
! 215: page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match)
! 216: {
! 217: static const struct dbm_match *match;
! 218: static const char *cp;
! 219: static int32_t ip;
! 220: struct dbm_res res = {-1, 0};
! 221:
! 222: assert(arg_iter == ITER_NAME || arg_iter == ITER_DESC ||
! 223: arg_iter == ITER_SECT);
! 224:
! 225: /* Initialize for a new iteration. */
! 226:
! 227: if (arg_match != NULL) {
! 228: iteration = arg_iter;
! 229: match = arg_match;
! 230: switch (iteration) {
! 231: case ITER_NAME:
! 232: cp = dbm_get(pages[0].name);
! 233: break;
! 234: case ITER_SECT:
! 235: cp = dbm_get(pages[0].sect);
! 236: break;
! 237: case ITER_DESC:
! 238: cp = dbm_get(pages[0].desc);
! 239: break;
! 240: default:
! 241: abort();
! 242: }
! 243: ip = 0;
! 244: return res;
! 245: }
! 246:
! 247: /* Search for a name. */
! 248:
! 249: while (ip < npages) {
! 250: if (iteration == ITER_NAME)
! 251: cp++;
! 252: if (dbm_match(match, cp))
! 253: break;
! 254: cp = strchr(cp, '\0') + 1;
! 255: if (iteration == ITER_DESC)
! 256: ip++;
! 257: else if (*cp == '\0') {
! 258: cp++;
! 259: ip++;
! 260: }
! 261: }
! 262:
! 263: /* Reached the end without a match. */
! 264:
! 265: if (ip == npages) {
! 266: iteration = ITER_NONE;
! 267: match = NULL;
! 268: cp = NULL;
! 269: return res;
! 270: }
! 271:
! 272: /* Found a match; save the quality for later retrieval. */
! 273:
! 274: res.page = ip;
! 275: res.bits = iteration == ITER_NAME ? cp[-1] : 0;
! 276:
! 277: /* Skip the remaining names of this page. */
! 278:
! 279: if (++ip < npages) {
! 280: do {
! 281: cp++;
! 282: } while (cp[-1] != '\0' ||
! 283: (iteration != ITER_DESC && cp[-2] != '\0'));
! 284: }
! 285: return res;
! 286: }
! 287:
! 288: static struct dbm_res
! 289: page_byarch(const struct dbm_match *arg_match)
! 290: {
! 291: static const struct dbm_match *match;
! 292: struct dbm_res res = {-1, 0};
! 293: static int32_t ip;
! 294: const char *cp;
! 295:
! 296: /* Initialize for a new iteration. */
! 297:
! 298: if (arg_match != NULL) {
! 299: iteration = ITER_ARCH;
! 300: match = arg_match;
! 301: ip = 0;
! 302: return res;
! 303: }
! 304:
! 305: /* Search for an architecture. */
! 306:
! 307: for ( ; ip < npages; ip++)
! 308: if (pages[ip].arch)
! 309: for (cp = dbm_get(pages[ip].arch);
! 310: *cp != '\0';
! 311: cp = strchr(cp, '\0') + 1)
! 312: if (dbm_match(match, cp)) {
! 313: res.page = ip++;
! 314: return res;
! 315: }
! 316:
! 317: /* Reached the end without a match. */
! 318:
! 319: iteration = ITER_NONE;
! 320: match = NULL;
! 321: return res;
! 322: }
! 323:
! 324: static struct dbm_res
! 325: page_bymacro(int32_t im, const struct dbm_match *match)
! 326: {
! 327: static const int32_t *pp;
! 328: struct dbm_res res = {-1, 0};
! 329: const char *cp;
! 330: int32_t iv;
! 331:
! 332: assert(im >= 0);
! 333: assert(im < MACRO_MAX);
! 334:
! 335: /* Initialize for a new iteration. */
! 336:
! 337: if (match != NULL) {
! 338: iteration = ITER_MACRO;
! 339: cp = nvals[im] ? dbm_get(macros[im]->value) : NULL;
! 340: for (iv = 0; iv < nvals[im]; iv++) {
! 341: if (dbm_match(match, cp))
! 342: break;
! 343: cp = strchr(cp, '\0') + 1;
! 344: }
! 345: pp = iv == nvals[im] ? NULL : dbm_get(macros[im][iv].pages);
! 346: return res;
! 347: }
! 348: if (iteration != ITER_MACRO)
! 349: return res;
! 350:
! 351: /* No more matches. */
! 352:
! 353: if (pp == NULL || *pp == 0) {
! 354: iteration = ITER_NONE;
! 355: pp = NULL;
! 356: return res;
! 357: }
! 358:
! 359: /* Found a match. */
! 360:
! 361: res.page = (struct page *)dbm_get(*pp++) - pages;
! 362: return res;
! 363: }
! 364:
! 365:
! 366: /*** functions for handling macros ************************************/
! 367:
! 368: int32_t
! 369: dbm_macro_count(int32_t im)
! 370: {
! 371: assert(im >= 0);
! 372: assert(im < MACRO_MAX);
! 373: return nvals[im];
! 374: }
! 375:
! 376: struct dbm_macro *
! 377: dbm_macro_get(int32_t im, int32_t iv)
! 378: {
! 379: static struct dbm_macro macro;
! 380:
! 381: assert(im >= 0);
! 382: assert(im < MACRO_MAX);
! 383: assert(iv >= 0);
! 384: assert(iv < nvals[im]);
! 385: macro.value = dbm_get(macros[im][iv].value);
! 386: macro.pp = dbm_get(macros[im][iv].pages);
! 387: return ¯o;
! 388: }
! 389:
! 390: /*
! 391: * Filtered iteration over macro entries.
! 392: */
! 393: void
! 394: dbm_macro_bypage(int32_t im, int32_t ip)
! 395: {
! 396: assert(im >= 0);
! 397: assert(im < MACRO_MAX);
! 398: assert(ip != 0);
! 399: macro_bypage(im, ip);
! 400: }
! 401:
! 402: char *
! 403: dbm_macro_next(void)
! 404: {
! 405: return macro_bypage(MACRO_MAX, 0);
! 406: }
! 407:
! 408: static char *
! 409: macro_bypage(int32_t arg_im, int32_t arg_ip)
! 410: {
! 411: static const int32_t *pp;
! 412: static int32_t im, ip, iv;
! 413:
! 414: /* Initialize for a new iteration. */
! 415:
! 416: if (arg_im < MACRO_MAX && arg_ip != 0) {
! 417: im = arg_im;
! 418: ip = arg_ip;
! 419: pp = dbm_get(macros[im]->pages);
! 420: iv = 0;
! 421: return NULL;
! 422: }
! 423: if (im >= MACRO_MAX)
! 424: return NULL;
! 425:
! 426: /* Search for the next value. */
! 427:
! 428: while (iv < nvals[im]) {
! 429: if (*pp == ip)
! 430: break;
! 431: if (*pp == 0)
! 432: iv++;
! 433: pp++;
! 434: }
! 435:
! 436: /* Reached the end without a match. */
! 437:
! 438: if (iv == nvals[im]) {
! 439: im = MACRO_MAX;
! 440: ip = 0;
! 441: pp = NULL;
! 442: return NULL;
! 443: }
! 444:
! 445: /* Found a match; skip the remaining pages of this entry. */
! 446:
! 447: if (++iv < nvals[im])
! 448: while (*pp++ != 0)
! 449: continue;
! 450:
! 451: return dbm_get(macros[im][iv - 1].value);
! 452: }
CVSweb