xref: /openbsd-src/usr.bin/mandoc/dbm.c (revision c8dfc550b604bcb7ee3aabea36cd824a29406dda)
1*c8dfc550Sschwarze /*	$OpenBSD: dbm.c,v 1.5 2019/07/01 22:43:03 schwarze Exp $ */
2ff2dbb0fSschwarze /*
3ff2dbb0fSschwarze  * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
4ff2dbb0fSschwarze  *
5ff2dbb0fSschwarze  * Permission to use, copy, modify, and distribute this software for any
6ff2dbb0fSschwarze  * purpose with or without fee is hereby granted, provided that the above
7ff2dbb0fSschwarze  * copyright notice and this permission notice appear in all copies.
8ff2dbb0fSschwarze  *
9ff2dbb0fSschwarze  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10ff2dbb0fSschwarze  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11ff2dbb0fSschwarze  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12ff2dbb0fSschwarze  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13ff2dbb0fSschwarze  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14ff2dbb0fSschwarze  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15ff2dbb0fSschwarze  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
16ff2dbb0fSschwarze  *
17ff2dbb0fSschwarze  * Map-based version of the mandoc database, for read-only access.
18ff2dbb0fSschwarze  * The interface is defined in "dbm.h".
19ff2dbb0fSschwarze  */
20ff2dbb0fSschwarze #include <assert.h>
21ff2dbb0fSschwarze #include <endian.h>
22ff2dbb0fSschwarze #include <err.h>
23ff2dbb0fSschwarze #include <errno.h>
24ff2dbb0fSschwarze #include <regex.h>
25ff2dbb0fSschwarze #include <stdint.h>
26ff2dbb0fSschwarze #include <stdio.h>
27ff2dbb0fSschwarze #include <stdlib.h>
28ff2dbb0fSschwarze #include <string.h>
29ff2dbb0fSschwarze 
30ff2dbb0fSschwarze #include "mansearch.h"
31ff2dbb0fSschwarze #include "dbm_map.h"
32ff2dbb0fSschwarze #include "dbm.h"
33ff2dbb0fSschwarze 
34ff2dbb0fSschwarze struct macro {
35ff2dbb0fSschwarze 	int32_t	value;
36ff2dbb0fSschwarze 	int32_t	pages;
37ff2dbb0fSschwarze };
38ff2dbb0fSschwarze 
39ff2dbb0fSschwarze struct page {
40ff2dbb0fSschwarze 	int32_t	name;
41ff2dbb0fSschwarze 	int32_t	sect;
42ff2dbb0fSschwarze 	int32_t	arch;
43ff2dbb0fSschwarze 	int32_t	desc;
44ff2dbb0fSschwarze 	int32_t	file;
45ff2dbb0fSschwarze };
46ff2dbb0fSschwarze 
47ff2dbb0fSschwarze enum iter {
48ff2dbb0fSschwarze 	ITER_NONE = 0,
49ff2dbb0fSschwarze 	ITER_NAME,
50ff2dbb0fSschwarze 	ITER_SECT,
51ff2dbb0fSschwarze 	ITER_ARCH,
52ff2dbb0fSschwarze 	ITER_DESC,
53ff2dbb0fSschwarze 	ITER_MACRO
54ff2dbb0fSschwarze };
55ff2dbb0fSschwarze 
56ff2dbb0fSschwarze static struct macro	*macros[MACRO_MAX];
57ff2dbb0fSschwarze static int32_t		 nvals[MACRO_MAX];
58ff2dbb0fSschwarze static struct page	*pages;
59ff2dbb0fSschwarze static int32_t		 npages;
60ff2dbb0fSschwarze static enum iter	 iteration;
61ff2dbb0fSschwarze 
62ff2dbb0fSschwarze static struct dbm_res	 page_bytitle(enum iter, const struct dbm_match *);
63ff2dbb0fSschwarze static struct dbm_res	 page_byarch(const struct dbm_match *);
64ff2dbb0fSschwarze static struct dbm_res	 page_bymacro(int32_t, const struct dbm_match *);
65ff2dbb0fSschwarze static char		*macro_bypage(int32_t, int32_t);
66ff2dbb0fSschwarze 
67ff2dbb0fSschwarze 
68ff2dbb0fSschwarze /*** top level functions **********************************************/
69ff2dbb0fSschwarze 
70ff2dbb0fSschwarze /*
71ff2dbb0fSschwarze  * Open a disk-based mandoc database for read-only access.
72ff2dbb0fSschwarze  * Map the pages and macros[] arrays.
73ff2dbb0fSschwarze  * Return 0 on success.  Return -1 and set errno on failure.
74ff2dbb0fSschwarze  */
75ff2dbb0fSschwarze int
dbm_open(const char * fname)76ff2dbb0fSschwarze dbm_open(const char *fname)
77ff2dbb0fSschwarze {
78ff2dbb0fSschwarze 	const int32_t	*mp, *ep;
79ff2dbb0fSschwarze 	int32_t		 im;
80ff2dbb0fSschwarze 
81ff2dbb0fSschwarze 	if (dbm_map(fname) == -1)
82ff2dbb0fSschwarze 		return -1;
83ff2dbb0fSschwarze 
84ff2dbb0fSschwarze 	if ((npages = be32toh(*dbm_getint(4))) < 0) {
85ff2dbb0fSschwarze 		warnx("dbm_open(%s): Invalid number of pages: %d",
86ff2dbb0fSschwarze 		    fname, npages);
87ff2dbb0fSschwarze 		goto fail;
88ff2dbb0fSschwarze 	}
89ff2dbb0fSschwarze 	pages = (struct page *)dbm_getint(5);
90ff2dbb0fSschwarze 
91ff2dbb0fSschwarze 	if ((mp = dbm_get(*dbm_getint(2))) == NULL) {
92ff2dbb0fSschwarze 		warnx("dbm_open(%s): Invalid offset of macros array", fname);
93ff2dbb0fSschwarze 		goto fail;
94ff2dbb0fSschwarze 	}
95ff2dbb0fSschwarze 	if (be32toh(*mp) != MACRO_MAX) {
96ff2dbb0fSschwarze 		warnx("dbm_open(%s): Invalid number of macros: %d",
97ff2dbb0fSschwarze 		    fname, be32toh(*mp));
98ff2dbb0fSschwarze 		goto fail;
99ff2dbb0fSschwarze 	}
100ff2dbb0fSschwarze 	for (im = 0; im < MACRO_MAX; im++) {
101ff2dbb0fSschwarze 		if ((ep = dbm_get(*++mp)) == NULL) {
102ff2dbb0fSschwarze 			warnx("dbm_open(%s): Invalid offset of macro %d",
103ff2dbb0fSschwarze 			    fname, im);
104ff2dbb0fSschwarze 			goto fail;
105ff2dbb0fSschwarze 		}
106ff2dbb0fSschwarze 		nvals[im] = be32toh(*ep);
107ff2dbb0fSschwarze 		macros[im] = (struct macro *)++ep;
108ff2dbb0fSschwarze 	}
109ff2dbb0fSschwarze 	return 0;
110ff2dbb0fSschwarze 
111ff2dbb0fSschwarze fail:
112ff2dbb0fSschwarze 	dbm_unmap();
113ff2dbb0fSschwarze 	errno = EFTYPE;
114ff2dbb0fSschwarze 	return -1;
115ff2dbb0fSschwarze }
116ff2dbb0fSschwarze 
117ff2dbb0fSschwarze void
dbm_close(void)118ff2dbb0fSschwarze dbm_close(void)
119ff2dbb0fSschwarze {
120ff2dbb0fSschwarze 	dbm_unmap();
121ff2dbb0fSschwarze }
122ff2dbb0fSschwarze 
123ff2dbb0fSschwarze 
124ff2dbb0fSschwarze /*** functions for handling pages *************************************/
125ff2dbb0fSschwarze 
126ff2dbb0fSschwarze int32_t
dbm_page_count(void)127ff2dbb0fSschwarze dbm_page_count(void)
128ff2dbb0fSschwarze {
129ff2dbb0fSschwarze 	return npages;
130ff2dbb0fSschwarze }
131ff2dbb0fSschwarze 
132ff2dbb0fSschwarze /*
133ff2dbb0fSschwarze  * Give the caller pointers to the data for one manual page.
134ff2dbb0fSschwarze  */
135ff2dbb0fSschwarze struct dbm_page *
dbm_page_get(int32_t ip)136ff2dbb0fSschwarze dbm_page_get(int32_t ip)
137ff2dbb0fSschwarze {
138ff2dbb0fSschwarze 	static struct dbm_page	 res;
139ff2dbb0fSschwarze 
140ff2dbb0fSschwarze 	assert(ip >= 0);
141ff2dbb0fSschwarze 	assert(ip < npages);
142ff2dbb0fSschwarze 	res.name = dbm_get(pages[ip].name);
143e252f3d9Sschwarze 	if (res.name == NULL)
144785207f8Sbluhm 		res.name = "(NULL)\0";
145ff2dbb0fSschwarze 	res.sect = dbm_get(pages[ip].sect);
146e252f3d9Sschwarze 	if (res.sect == NULL)
147785207f8Sbluhm 		res.sect = "(NULL)\0";
148ff2dbb0fSschwarze 	res.arch = pages[ip].arch ? dbm_get(pages[ip].arch) : NULL;
149ff2dbb0fSschwarze 	res.desc = dbm_get(pages[ip].desc);
150e252f3d9Sschwarze 	if (res.desc == NULL)
151e252f3d9Sschwarze 		res.desc = "(NULL)";
152ff2dbb0fSschwarze 	res.file = dbm_get(pages[ip].file);
153e252f3d9Sschwarze 	if (res.file == NULL)
154785207f8Sbluhm 		res.file = " (NULL)\0";
155ff2dbb0fSschwarze 	res.addr = dbm_addr(pages + ip);
156ff2dbb0fSschwarze 	return &res;
157ff2dbb0fSschwarze }
158ff2dbb0fSschwarze 
159ff2dbb0fSschwarze /*
160ff2dbb0fSschwarze  * Functions to start filtered iterations over manual pages.
161ff2dbb0fSschwarze  */
162ff2dbb0fSschwarze void
dbm_page_byname(const struct dbm_match * match)163ff2dbb0fSschwarze dbm_page_byname(const struct dbm_match *match)
164ff2dbb0fSschwarze {
165ff2dbb0fSschwarze 	assert(match != NULL);
166ff2dbb0fSschwarze 	page_bytitle(ITER_NAME, match);
167ff2dbb0fSschwarze }
168ff2dbb0fSschwarze 
169ff2dbb0fSschwarze void
dbm_page_bysect(const struct dbm_match * match)170ff2dbb0fSschwarze dbm_page_bysect(const struct dbm_match *match)
171ff2dbb0fSschwarze {
172ff2dbb0fSschwarze 	assert(match != NULL);
173ff2dbb0fSschwarze 	page_bytitle(ITER_SECT, match);
174ff2dbb0fSschwarze }
175ff2dbb0fSschwarze 
176ff2dbb0fSschwarze void
dbm_page_byarch(const struct dbm_match * match)177ff2dbb0fSschwarze dbm_page_byarch(const struct dbm_match *match)
178ff2dbb0fSschwarze {
179ff2dbb0fSschwarze 	assert(match != NULL);
180ff2dbb0fSschwarze 	page_byarch(match);
181ff2dbb0fSschwarze }
182ff2dbb0fSschwarze 
183ff2dbb0fSschwarze void
dbm_page_bydesc(const struct dbm_match * match)184ff2dbb0fSschwarze dbm_page_bydesc(const struct dbm_match *match)
185ff2dbb0fSschwarze {
186ff2dbb0fSschwarze 	assert(match != NULL);
187ff2dbb0fSschwarze 	page_bytitle(ITER_DESC, match);
188ff2dbb0fSschwarze }
189ff2dbb0fSschwarze 
190ff2dbb0fSschwarze void
dbm_page_bymacro(int32_t im,const struct dbm_match * match)191ff2dbb0fSschwarze dbm_page_bymacro(int32_t im, const struct dbm_match *match)
192ff2dbb0fSschwarze {
193ff2dbb0fSschwarze 	assert(im >= 0);
194ff2dbb0fSschwarze 	assert(im < MACRO_MAX);
195ff2dbb0fSschwarze 	assert(match != NULL);
196ff2dbb0fSschwarze 	page_bymacro(im, match);
197ff2dbb0fSschwarze }
198ff2dbb0fSschwarze 
199ff2dbb0fSschwarze /*
200ff2dbb0fSschwarze  * Return the number of the next manual page in the current iteration.
201ff2dbb0fSschwarze  */
202ff2dbb0fSschwarze struct dbm_res
dbm_page_next(void)203ff2dbb0fSschwarze dbm_page_next(void)
204ff2dbb0fSschwarze {
205ff2dbb0fSschwarze 	struct dbm_res			 res = {-1, 0};
206ff2dbb0fSschwarze 
207ff2dbb0fSschwarze 	switch(iteration) {
208ff2dbb0fSschwarze 	case ITER_NONE:
209ff2dbb0fSschwarze 		return res;
210ff2dbb0fSschwarze 	case ITER_ARCH:
211ff2dbb0fSschwarze 		return page_byarch(NULL);
212ff2dbb0fSschwarze 	case ITER_MACRO:
213ff2dbb0fSschwarze 		return page_bymacro(0, NULL);
214ff2dbb0fSschwarze 	default:
215ff2dbb0fSschwarze 		return page_bytitle(iteration, NULL);
216ff2dbb0fSschwarze 	}
217ff2dbb0fSschwarze }
218ff2dbb0fSschwarze 
219ff2dbb0fSschwarze /*
220ff2dbb0fSschwarze  * Functions implementing the iteration over manual pages.
221ff2dbb0fSschwarze  */
222ff2dbb0fSschwarze static struct dbm_res
page_bytitle(enum iter arg_iter,const struct dbm_match * arg_match)223ff2dbb0fSschwarze page_bytitle(enum iter arg_iter, const struct dbm_match *arg_match)
224ff2dbb0fSschwarze {
225ff2dbb0fSschwarze 	static const struct dbm_match	*match;
226ff2dbb0fSschwarze 	static const char		*cp;
227ff2dbb0fSschwarze 	static int32_t			 ip;
228ff2dbb0fSschwarze 	struct dbm_res			 res = {-1, 0};
229ff2dbb0fSschwarze 
230ff2dbb0fSschwarze 	assert(arg_iter == ITER_NAME || arg_iter == ITER_DESC ||
231ff2dbb0fSschwarze 	    arg_iter == ITER_SECT);
232ff2dbb0fSschwarze 
233ff2dbb0fSschwarze 	/* Initialize for a new iteration. */
234ff2dbb0fSschwarze 
235ff2dbb0fSschwarze 	if (arg_match != NULL) {
236ff2dbb0fSschwarze 		iteration = arg_iter;
237ff2dbb0fSschwarze 		match = arg_match;
238ff2dbb0fSschwarze 		switch (iteration) {
239ff2dbb0fSschwarze 		case ITER_NAME:
240ff2dbb0fSschwarze 			cp = dbm_get(pages[0].name);
241ff2dbb0fSschwarze 			break;
242ff2dbb0fSschwarze 		case ITER_SECT:
243ff2dbb0fSschwarze 			cp = dbm_get(pages[0].sect);
244ff2dbb0fSschwarze 			break;
245ff2dbb0fSschwarze 		case ITER_DESC:
246ff2dbb0fSschwarze 			cp = dbm_get(pages[0].desc);
247ff2dbb0fSschwarze 			break;
248ff2dbb0fSschwarze 		default:
249ff2dbb0fSschwarze 			abort();
250ff2dbb0fSschwarze 		}
251e252f3d9Sschwarze 		if (cp == NULL) {
252e252f3d9Sschwarze 			iteration = ITER_NONE;
253e252f3d9Sschwarze 			match = NULL;
254e252f3d9Sschwarze 			cp = NULL;
255e252f3d9Sschwarze 			ip = npages;
256e252f3d9Sschwarze 		} else
257ff2dbb0fSschwarze 			ip = 0;
258ff2dbb0fSschwarze 		return res;
259ff2dbb0fSschwarze 	}
260ff2dbb0fSschwarze 
261ff2dbb0fSschwarze 	/* Search for a name. */
262ff2dbb0fSschwarze 
263ff2dbb0fSschwarze 	while (ip < npages) {
264ff2dbb0fSschwarze 		if (iteration == ITER_NAME)
265ff2dbb0fSschwarze 			cp++;
266ff2dbb0fSschwarze 		if (dbm_match(match, cp))
267ff2dbb0fSschwarze 			break;
268ff2dbb0fSschwarze 		cp = strchr(cp, '\0') + 1;
269ff2dbb0fSschwarze 		if (iteration == ITER_DESC)
270ff2dbb0fSschwarze 			ip++;
271ff2dbb0fSschwarze 		else if (*cp == '\0') {
272ff2dbb0fSschwarze 			cp++;
273ff2dbb0fSschwarze 			ip++;
274ff2dbb0fSschwarze 		}
275ff2dbb0fSschwarze 	}
276ff2dbb0fSschwarze 
277ff2dbb0fSschwarze 	/* Reached the end without a match. */
278ff2dbb0fSschwarze 
279ff2dbb0fSschwarze 	if (ip == npages) {
280ff2dbb0fSschwarze 		iteration = ITER_NONE;
281ff2dbb0fSschwarze 		match = NULL;
282ff2dbb0fSschwarze 		cp = NULL;
283ff2dbb0fSschwarze 		return res;
284ff2dbb0fSschwarze 	}
285ff2dbb0fSschwarze 
286ff2dbb0fSschwarze 	/* Found a match; save the quality for later retrieval. */
287ff2dbb0fSschwarze 
288ff2dbb0fSschwarze 	res.page = ip;
289ff2dbb0fSschwarze 	res.bits = iteration == ITER_NAME ? cp[-1] : 0;
290ff2dbb0fSschwarze 
291ff2dbb0fSschwarze 	/* Skip the remaining names of this page. */
292ff2dbb0fSschwarze 
293ff2dbb0fSschwarze 	if (++ip < npages) {
294ff2dbb0fSschwarze 		do {
295ff2dbb0fSschwarze 			cp++;
296ff2dbb0fSschwarze 		} while (cp[-1] != '\0' ||
297ff2dbb0fSschwarze 		    (iteration != ITER_DESC && cp[-2] != '\0'));
298ff2dbb0fSschwarze 	}
299ff2dbb0fSschwarze 	return res;
300ff2dbb0fSschwarze }
301ff2dbb0fSschwarze 
302ff2dbb0fSschwarze static struct dbm_res
page_byarch(const struct dbm_match * arg_match)303ff2dbb0fSschwarze page_byarch(const struct dbm_match *arg_match)
304ff2dbb0fSschwarze {
305ff2dbb0fSschwarze 	static const struct dbm_match	*match;
306ff2dbb0fSschwarze 	struct dbm_res			 res = {-1, 0};
307ff2dbb0fSschwarze 	static int32_t			 ip;
308ff2dbb0fSschwarze 	const char			*cp;
309ff2dbb0fSschwarze 
310ff2dbb0fSschwarze 	/* Initialize for a new iteration. */
311ff2dbb0fSschwarze 
312ff2dbb0fSschwarze 	if (arg_match != NULL) {
313ff2dbb0fSschwarze 		iteration = ITER_ARCH;
314ff2dbb0fSschwarze 		match = arg_match;
315ff2dbb0fSschwarze 		ip = 0;
316ff2dbb0fSschwarze 		return res;
317ff2dbb0fSschwarze 	}
318ff2dbb0fSschwarze 
319ff2dbb0fSschwarze 	/* Search for an architecture. */
320ff2dbb0fSschwarze 
321ff2dbb0fSschwarze 	for ( ; ip < npages; ip++)
322ff2dbb0fSschwarze 		if (pages[ip].arch)
323ff2dbb0fSschwarze 			for (cp = dbm_get(pages[ip].arch);
324ff2dbb0fSschwarze 			    *cp != '\0';
325ff2dbb0fSschwarze 			    cp = strchr(cp, '\0') + 1)
326ff2dbb0fSschwarze 				if (dbm_match(match, cp)) {
327ff2dbb0fSschwarze 					res.page = ip++;
328ff2dbb0fSschwarze 					return res;
329ff2dbb0fSschwarze 				}
330ff2dbb0fSschwarze 
331ff2dbb0fSschwarze 	/* Reached the end without a match. */
332ff2dbb0fSschwarze 
333ff2dbb0fSschwarze 	iteration = ITER_NONE;
334ff2dbb0fSschwarze 	match = NULL;
335ff2dbb0fSschwarze 	return res;
336ff2dbb0fSschwarze }
337ff2dbb0fSschwarze 
338ff2dbb0fSschwarze static struct dbm_res
page_bymacro(int32_t arg_im,const struct dbm_match * arg_match)3394fb89dd8Sschwarze page_bymacro(int32_t arg_im, const struct dbm_match *arg_match)
340ff2dbb0fSschwarze {
3414fb89dd8Sschwarze 	static const struct dbm_match	*match;
342ff2dbb0fSschwarze 	static const int32_t		*pp;
3434fb89dd8Sschwarze 	static const char		*cp;
3444fb89dd8Sschwarze 	static int32_t			 im, iv;
345ff2dbb0fSschwarze 	struct dbm_res			 res = {-1, 0};
346ff2dbb0fSschwarze 
347ff2dbb0fSschwarze 	assert(im >= 0);
348ff2dbb0fSschwarze 	assert(im < MACRO_MAX);
349ff2dbb0fSschwarze 
350ff2dbb0fSschwarze 	/* Initialize for a new iteration. */
351ff2dbb0fSschwarze 
3524fb89dd8Sschwarze 	if (arg_match != NULL) {
353ff2dbb0fSschwarze 		iteration = ITER_MACRO;
3544fb89dd8Sschwarze 		match = arg_match;
3554fb89dd8Sschwarze 		im = arg_im;
356ff2dbb0fSschwarze 		cp = nvals[im] ? dbm_get(macros[im]->value) : NULL;
3574fb89dd8Sschwarze 		pp = NULL;
3584fb89dd8Sschwarze 		iv = -1;
359ff2dbb0fSschwarze 		return res;
360ff2dbb0fSschwarze 	}
361ff2dbb0fSschwarze 	if (iteration != ITER_MACRO)
362ff2dbb0fSschwarze 		return res;
363ff2dbb0fSschwarze 
3644fb89dd8Sschwarze 	/* Find the next matching macro value. */
365ff2dbb0fSschwarze 
3664fb89dd8Sschwarze 	while (pp == NULL || *pp == 0) {
3674fb89dd8Sschwarze 		if (++iv == nvals[im]) {
368ff2dbb0fSschwarze 			iteration = ITER_NONE;
369ff2dbb0fSschwarze 			return res;
370ff2dbb0fSschwarze 		}
3714fb89dd8Sschwarze 		if (iv)
3724fb89dd8Sschwarze 			cp = strchr(cp, '\0') + 1;
3734fb89dd8Sschwarze 		if (dbm_match(match, cp))
3744fb89dd8Sschwarze 			pp = dbm_get(macros[im][iv].pages);
3754fb89dd8Sschwarze 	}
376ff2dbb0fSschwarze 
3774fb89dd8Sschwarze 	/* Found a matching page. */
378ff2dbb0fSschwarze 
379ff2dbb0fSschwarze 	res.page = (struct page *)dbm_get(*pp++) - pages;
380ff2dbb0fSschwarze 	return res;
381ff2dbb0fSschwarze }
382ff2dbb0fSschwarze 
383ff2dbb0fSschwarze 
384ff2dbb0fSschwarze /*** functions for handling macros ************************************/
385ff2dbb0fSschwarze 
386ff2dbb0fSschwarze int32_t
dbm_macro_count(int32_t im)387ff2dbb0fSschwarze dbm_macro_count(int32_t im)
388ff2dbb0fSschwarze {
389ff2dbb0fSschwarze 	assert(im >= 0);
390ff2dbb0fSschwarze 	assert(im < MACRO_MAX);
391ff2dbb0fSschwarze 	return nvals[im];
392ff2dbb0fSschwarze }
393ff2dbb0fSschwarze 
394ff2dbb0fSschwarze struct dbm_macro *
dbm_macro_get(int32_t im,int32_t iv)395ff2dbb0fSschwarze dbm_macro_get(int32_t im, int32_t iv)
396ff2dbb0fSschwarze {
397ff2dbb0fSschwarze 	static struct dbm_macro macro;
398ff2dbb0fSschwarze 
399ff2dbb0fSschwarze 	assert(im >= 0);
400ff2dbb0fSschwarze 	assert(im < MACRO_MAX);
401ff2dbb0fSschwarze 	assert(iv >= 0);
402ff2dbb0fSschwarze 	assert(iv < nvals[im]);
403ff2dbb0fSschwarze 	macro.value = dbm_get(macros[im][iv].value);
404ff2dbb0fSschwarze 	macro.pp = dbm_get(macros[im][iv].pages);
405ff2dbb0fSschwarze 	return &macro;
406ff2dbb0fSschwarze }
407ff2dbb0fSschwarze 
408ff2dbb0fSschwarze /*
409ff2dbb0fSschwarze  * Filtered iteration over macro entries.
410ff2dbb0fSschwarze  */
411ff2dbb0fSschwarze void
dbm_macro_bypage(int32_t im,int32_t ip)412ff2dbb0fSschwarze dbm_macro_bypage(int32_t im, int32_t ip)
413ff2dbb0fSschwarze {
414ff2dbb0fSschwarze 	assert(im >= 0);
415ff2dbb0fSschwarze 	assert(im < MACRO_MAX);
416ff2dbb0fSschwarze 	assert(ip != 0);
417ff2dbb0fSschwarze 	macro_bypage(im, ip);
418ff2dbb0fSschwarze }
419ff2dbb0fSschwarze 
420ff2dbb0fSschwarze char *
dbm_macro_next(void)421ff2dbb0fSschwarze dbm_macro_next(void)
422ff2dbb0fSschwarze {
423ff2dbb0fSschwarze 	return macro_bypage(MACRO_MAX, 0);
424ff2dbb0fSschwarze }
425ff2dbb0fSschwarze 
426ff2dbb0fSschwarze static char *
macro_bypage(int32_t arg_im,int32_t arg_ip)427ff2dbb0fSschwarze macro_bypage(int32_t arg_im, int32_t arg_ip)
428ff2dbb0fSschwarze {
429ff2dbb0fSschwarze 	static const int32_t	*pp;
430ff2dbb0fSschwarze 	static int32_t		 im, ip, iv;
431ff2dbb0fSschwarze 
432ff2dbb0fSschwarze 	/* Initialize for a new iteration. */
433ff2dbb0fSschwarze 
434ff2dbb0fSschwarze 	if (arg_im < MACRO_MAX && arg_ip != 0) {
435ff2dbb0fSschwarze 		im = arg_im;
436ff2dbb0fSschwarze 		ip = arg_ip;
437ff2dbb0fSschwarze 		pp = dbm_get(macros[im]->pages);
438ff2dbb0fSschwarze 		iv = 0;
439ff2dbb0fSschwarze 		return NULL;
440ff2dbb0fSschwarze 	}
441ff2dbb0fSschwarze 	if (im >= MACRO_MAX)
442ff2dbb0fSschwarze 		return NULL;
443ff2dbb0fSschwarze 
444ff2dbb0fSschwarze 	/* Search for the next value. */
445ff2dbb0fSschwarze 
446ff2dbb0fSschwarze 	while (iv < nvals[im]) {
447ff2dbb0fSschwarze 		if (*pp == ip)
448ff2dbb0fSschwarze 			break;
449ff2dbb0fSschwarze 		if (*pp == 0)
450ff2dbb0fSschwarze 			iv++;
451ff2dbb0fSschwarze 		pp++;
452ff2dbb0fSschwarze 	}
453ff2dbb0fSschwarze 
454ff2dbb0fSschwarze 	/* Reached the end without a match. */
455ff2dbb0fSschwarze 
456ff2dbb0fSschwarze 	if (iv == nvals[im]) {
457ff2dbb0fSschwarze 		im = MACRO_MAX;
458ff2dbb0fSschwarze 		ip = 0;
459ff2dbb0fSschwarze 		pp = NULL;
460ff2dbb0fSschwarze 		return NULL;
461ff2dbb0fSschwarze 	}
462ff2dbb0fSschwarze 
463ff2dbb0fSschwarze 	/* Found a match; skip the remaining pages of this entry. */
464ff2dbb0fSschwarze 
465ff2dbb0fSschwarze 	if (++iv < nvals[im])
466ff2dbb0fSschwarze 		while (*pp++ != 0)
467ff2dbb0fSschwarze 			continue;
468ff2dbb0fSschwarze 
469ff2dbb0fSschwarze 	return dbm_get(macros[im][iv - 1].value);
470ff2dbb0fSschwarze }
471