Import mandoc-1.14.5 and leave only the files we need.
[dragonfly.git] / contrib / mdocml / dbm.c
1 /*      $Id: dbm.c,v 1.6 2018/11/19 19:22:07 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 #if HAVE_ENDIAN
24 #include <endian.h>
25 #elif HAVE_SYS_ENDIAN
26 #include <sys/endian.h>
27 #elif HAVE_NTOHL
28 #include <arpa/inet.h>
29 #endif
30 #if HAVE_ERR
31 #include <err.h>
32 #endif
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         if (res.name == NULL)
154                 res.name = "(NULL)\0";
155         res.sect = dbm_get(pages[ip].sect);
156         if (res.sect == NULL)
157                 res.sect = "(NULL)\0";
158         res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
159         res.desc = dbm_get(pages[ip].desc);
160         if (res.desc == NULL)
161                 res.desc = "(NULL)";
162         res.file = dbm_get(pages[ip].file);
163         if (res.file == NULL)
164                 res.file = " (NULL)\0";
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                 }
261                 if (cp == NULL) {
262                         iteration = ITER_NONE;
263                         match = NULL;
264                         cp = NULL;
265                         ip = npages;
266                 } else
267                         ip = 0;
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 arg_im, const struct dbm_match *arg_match)
350 {
351         static const struct dbm_match   *match;
352         static const int32_t            *pp;
353         static const char               *cp;
354         static int32_t                   im, iv;
355         struct dbm_res                   res = {-1, 0};
356
357         assert(im >= 0);
358         assert(im < MACRO_MAX);
359
360         /* Initialize for a new iteration. */
361
362         if (arg_match != NULL) {
363                 iteration = ITER_MACRO;
364                 match = arg_match;
365                 im = arg_im;
366                 cp = nvals[im] ? dbm_get(macros[im]->value) : NULL;
367                 pp = NULL;
368                 iv = -1;
369                 return res;
370         }
371         if (iteration != ITER_MACRO)
372                 return res;
373
374         /* Find the next matching macro value. */
375
376         while (pp == NULL || *pp == 0) {
377                 if (++iv == nvals[im]) {
378                         iteration = ITER_NONE;
379                         return res;
380                 }
381                 if (iv)
382                         cp = strchr(cp, '\0') + 1;
383                 if (dbm_match(match, cp))
384                         pp = dbm_get(macros[im][iv].pages);
385         }
386
387         /* Found a matching page. */
388
389         res.page = (struct page *)dbm_get(*pp++) - pages;
390         return res;
391 }
392
393
394 /*** functions for handling macros ************************************/
395
396 int32_t
397 dbm_macro_count(int32_t im)
398 {
399         assert(im >= 0);
400         assert(im < MACRO_MAX);
401         return nvals[im];
402 }
403
404 struct dbm_macro *
405 dbm_macro_get(int32_t im, int32_t iv)
406 {
407         static struct dbm_macro macro;
408
409         assert(im >= 0);
410         assert(im < MACRO_MAX);
411         assert(iv >= 0);
412         assert(iv < nvals[im]);
413         macro.value = dbm_get(macros[im][iv].value);
414         macro.pp = dbm_get(macros[im][iv].pages);
415         return &macro;
416 }
417
418 /*
419  * Filtered iteration over macro entries.
420  */
421 void
422 dbm_macro_bypage(int32_t im, int32_t ip)
423 {
424         assert(im >= 0);
425         assert(im < MACRO_MAX);
426         assert(ip != 0);
427         macro_bypage(im, ip);
428 }
429
430 char *
431 dbm_macro_next(void)
432 {
433         return macro_bypage(MACRO_MAX, 0);
434 }
435
436 static char *
437 macro_bypage(int32_t arg_im, int32_t arg_ip)
438 {
439         static const int32_t    *pp;
440         static int32_t           im, ip, iv;
441
442         /* Initialize for a new iteration. */
443
444         if (arg_im < MACRO_MAX && arg_ip != 0) {
445                 im = arg_im;
446                 ip = arg_ip;
447                 pp = dbm_get(macros[im]->pages);
448                 iv = 0;
449                 return NULL;
450         }
451         if (im >= MACRO_MAX)
452                 return NULL;
453
454         /* Search for the next value. */
455
456         while (iv < nvals[im]) {
457                 if (*pp == ip)
458                         break;
459                 if (*pp == 0)
460                         iv++;
461                 pp++;
462         }
463
464         /* Reached the end without a match. */
465
466         if (iv == nvals[im]) {
467                 im = MACRO_MAX;
468                 ip = 0;
469                 pp = NULL;
470                 return NULL;
471         }
472
473         /* Found a match; skip the remaining pages of this entry. */
474
475         if (++iv < nvals[im])
476                 while (*pp++ != 0)
477                         continue;
478
479         return dbm_get(macros[im][iv - 1].value);
480 }