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