]> git.cameronkatri.com Git - mandoc.git/blob - dbm.c
POSIX allows PATH_MAX to not be defined, meaning "unlimited".
[mandoc.git] / dbm.c
1 /* $Id: dbm.c,v 1.2 2016/07/20 00:23:14 schwarze Exp $ */
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 "config.h"
21
22 #include <assert.h>
23 #include <endian.h>
24 #if HAVE_ERR
25 #include <err.h>
26 #endif
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 &macro;
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 }