Annotation of mandoc/dbm.c, Revision 1.3
1.3 ! schwarze 1: /* $Id: dbm.c,v 1.2 2016/07/20 00:23:14 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);
153: res.sect = dbm_get(pages[ip].sect);
154: res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
155: res.desc = dbm_get(pages[ip].desc);
156: res.file = dbm_get(pages[ip].file);
157: res.addr = dbm_addr(pages + ip);
158: return &res;
159: }
160:
161: /*
162: * Functions to start filtered iterations over manual pages.
163: */
164: void
165: dbm_page_byname(const struct dbm_match *match)
166: {
167: assert(match != NULL);
168: page_bytitle(ITER_NAME, match);
169: }
170:
171: void
172: dbm_page_bysect(const struct dbm_match *match)
173: {
174: assert(match != NULL);
175: page_bytitle(ITER_SECT, match);
176: }
177:
178: void
179: dbm_page_byarch(const struct dbm_match *match)
180: {
181: assert(match != NULL);
182: page_byarch(match);
183: }
184:
185: void
186: dbm_page_bydesc(const struct dbm_match *match)
187: {
188: assert(match != NULL);
189: page_bytitle(ITER_DESC, match);
190: }
191:
192: void
193: dbm_page_bymacro(int32_t im, const struct dbm_match *match)
194: {
195: assert(im >= 0);
196: assert(im < MACRO_MAX);
197: assert(match != NULL);
198: page_bymacro(im, match);
199: }
200:
201: /*
202: * Return the number of the next manual page in the current iteration.
203: */
204: struct dbm_res
205: dbm_page_next(void)
206: {
207: struct dbm_res res = {-1, 0};
208:
209: switch(iteration) {
210: case ITER_NONE:
211: return res;
212: case ITER_ARCH:
213: return page_byarch(NULL);
214: case ITER_MACRO:
215: return page_bymacro(0, NULL);
216: default:
217: return page_bytitle(iteration, NULL);
218: }
219: }
220:
221: /*
222: * Functions implementing the iteration over manual pages.
223: */
224: static struct dbm_res
225: page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match)
226: {
227: static const struct dbm_match *match;
228: static const char *cp;
229: static int32_t ip;
230: struct dbm_res res = {-1, 0};
231:
232: assert(arg_iter == ITER_NAME || arg_iter == ITER_DESC ||
233: arg_iter == ITER_SECT);
234:
235: /* Initialize for a new iteration. */
236:
237: if (arg_match != NULL) {
238: iteration = arg_iter;
239: match = arg_match;
240: switch (iteration) {
241: case ITER_NAME:
242: cp = dbm_get(pages[0].name);
243: break;
244: case ITER_SECT:
245: cp = dbm_get(pages[0].sect);
246: break;
247: case ITER_DESC:
248: cp = dbm_get(pages[0].desc);
249: break;
250: default:
251: abort();
252: }
253: ip = 0;
254: return res;
255: }
256:
257: /* Search for a name. */
258:
259: while (ip < npages) {
260: if (iteration == ITER_NAME)
261: cp++;
262: if (dbm_match(match, cp))
263: break;
264: cp = strchr(cp, '\0') + 1;
265: if (iteration == ITER_DESC)
266: ip++;
267: else if (*cp == '\0') {
268: cp++;
269: ip++;
270: }
271: }
272:
273: /* Reached the end without a match. */
274:
275: if (ip == npages) {
276: iteration = ITER_NONE;
277: match = NULL;
278: cp = NULL;
279: return res;
280: }
281:
282: /* Found a match; save the quality for later retrieval. */
283:
284: res.page = ip;
285: res.bits = iteration == ITER_NAME ? cp[-1] : 0;
286:
287: /* Skip the remaining names of this page. */
288:
289: if (++ip < npages) {
290: do {
291: cp++;
292: } while (cp[-1] != '\0' ||
293: (iteration != ITER_DESC && cp[-2] != '\0'));
294: }
295: return res;
296: }
297:
298: static struct dbm_res
299: page_byarch(const struct dbm_match *arg_match)
300: {
301: static const struct dbm_match *match;
302: struct dbm_res res = {-1, 0};
303: static int32_t ip;
304: const char *cp;
305:
306: /* Initialize for a new iteration. */
307:
308: if (arg_match != NULL) {
309: iteration = ITER_ARCH;
310: match = arg_match;
311: ip = 0;
312: return res;
313: }
314:
315: /* Search for an architecture. */
316:
317: for ( ; ip < npages; ip++)
318: if (pages[ip].arch)
319: for (cp = dbm_get(pages[ip].arch);
320: *cp != '\0';
321: cp = strchr(cp, '\0') + 1)
322: if (dbm_match(match, cp)) {
323: res.page = ip++;
324: return res;
325: }
326:
327: /* Reached the end without a match. */
328:
329: iteration = ITER_NONE;
330: match = NULL;
331: return res;
332: }
333:
334: static struct dbm_res
335: page_bymacro(int32_t im, const struct dbm_match *match)
336: {
337: static const int32_t *pp;
338: struct dbm_res res = {-1, 0};
339: const char *cp;
340: int32_t iv;
341:
342: assert(im >= 0);
343: assert(im < MACRO_MAX);
344:
345: /* Initialize for a new iteration. */
346:
347: if (match != NULL) {
348: iteration = ITER_MACRO;
349: cp = nvals[im] ? dbm_get(macros[im]->value) : NULL;
350: for (iv = 0; iv < nvals[im]; iv++) {
351: if (dbm_match(match, cp))
352: break;
353: cp = strchr(cp, '\0') + 1;
354: }
355: pp = iv == nvals[im] ? NULL : dbm_get(macros[im][iv].pages);
356: return res;
357: }
358: if (iteration != ITER_MACRO)
359: return res;
360:
361: /* No more matches. */
362:
363: if (pp == NULL || *pp == 0) {
364: iteration = ITER_NONE;
365: pp = NULL;
366: return res;
367: }
368:
369: /* Found a match. */
370:
371: res.page = (struct page *)dbm_get(*pp++) - pages;
372: return res;
373: }
374:
375:
376: /*** functions for handling macros ************************************/
377:
378: int32_t
379: dbm_macro_count(int32_t im)
380: {
381: assert(im >= 0);
382: assert(im < MACRO_MAX);
383: return nvals[im];
384: }
385:
386: struct dbm_macro *
387: dbm_macro_get(int32_t im, int32_t iv)
388: {
389: static struct dbm_macro macro;
390:
391: assert(im >= 0);
392: assert(im < MACRO_MAX);
393: assert(iv >= 0);
394: assert(iv < nvals[im]);
395: macro.value = dbm_get(macros[im][iv].value);
396: macro.pp = dbm_get(macros[im][iv].pages);
397: return ¯o;
398: }
399:
400: /*
401: * Filtered iteration over macro entries.
402: */
403: void
404: dbm_macro_bypage(int32_t im, int32_t ip)
405: {
406: assert(im >= 0);
407: assert(im < MACRO_MAX);
408: assert(ip != 0);
409: macro_bypage(im, ip);
410: }
411:
412: char *
413: dbm_macro_next(void)
414: {
415: return macro_bypage(MACRO_MAX, 0);
416: }
417:
418: static char *
419: macro_bypage(int32_t arg_im, int32_t arg_ip)
420: {
421: static const int32_t *pp;
422: static int32_t im, ip, iv;
423:
424: /* Initialize for a new iteration. */
425:
426: if (arg_im < MACRO_MAX && arg_ip != 0) {
427: im = arg_im;
428: ip = arg_ip;
429: pp = dbm_get(macros[im]->pages);
430: iv = 0;
431: return NULL;
432: }
433: if (im >= MACRO_MAX)
434: return NULL;
435:
436: /* Search for the next value. */
437:
438: while (iv < nvals[im]) {
439: if (*pp == ip)
440: break;
441: if (*pp == 0)
442: iv++;
443: pp++;
444: }
445:
446: /* Reached the end without a match. */
447:
448: if (iv == nvals[im]) {
449: im = MACRO_MAX;
450: ip = 0;
451: pp = NULL;
452: return NULL;
453: }
454:
455: /* Found a match; skip the remaining pages of this entry. */
456:
457: if (++iv < nvals[im])
458: while (*pp++ != 0)
459: continue;
460:
461: return dbm_get(macros[im][iv - 1].value);
462: }
CVSweb