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