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