xref: /openbsd-src/usr.bin/mandoc/dba.c (revision 79a81166797a360cce61a4013d6bf48c3d0bcbbb)
1*79a81166Sschwarze /*	$OpenBSD: dba.c,v 1.7 2017/02/09 18:26:17 schwarze Exp $ */
2ff2dbb0fSschwarze /*
33fc2cf7eSschwarze  * Copyright (c) 2016, 2017 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  * Allocation-based version of the mandoc database, for read-write access.
18ff2dbb0fSschwarze  * The interface is defined in "dba.h".
19ff2dbb0fSschwarze  */
20ff2dbb0fSschwarze #include <sys/types.h>
2176742941Sschwarze #include <endian.h>
22ff2dbb0fSschwarze #include <errno.h>
233fc2cf7eSschwarze #include <stddef.h>
24ff2dbb0fSschwarze #include <stdint.h>
25ff2dbb0fSschwarze #include <stdlib.h>
26ff2dbb0fSschwarze #include <string.h>
27ff2dbb0fSschwarze #include <unistd.h>
28ff2dbb0fSschwarze 
29ff2dbb0fSschwarze #include "mandoc_aux.h"
303fc2cf7eSschwarze #include "mandoc_ohash.h"
31ff2dbb0fSschwarze #include "mansearch.h"
32ff2dbb0fSschwarze #include "dba_write.h"
33ff2dbb0fSschwarze #include "dba_array.h"
34ff2dbb0fSschwarze #include "dba.h"
35ff2dbb0fSschwarze 
363fc2cf7eSschwarze struct macro_entry {
373fc2cf7eSschwarze 	struct dba_array	*pages;
383fc2cf7eSschwarze 	char			 value[];
393fc2cf7eSschwarze };
403fc2cf7eSschwarze 
41ff2dbb0fSschwarze static void	*prepend(const char *, char);
42ff2dbb0fSschwarze static void	 dba_pages_write(struct dba_array *);
43ff2dbb0fSschwarze static int	 compare_names(const void *, const void *);
44ff2dbb0fSschwarze static int	 compare_strings(const void *, const void *);
453fc2cf7eSschwarze 
463fc2cf7eSschwarze static struct macro_entry
473fc2cf7eSschwarze 		*get_macro_entry(struct ohash *, const char *, int32_t);
48ff2dbb0fSschwarze static void	 dba_macros_write(struct dba_array *);
493fc2cf7eSschwarze static void	 dba_macro_write(struct ohash *);
503fc2cf7eSschwarze static int	 compare_entries(const void *, const void *);
51ff2dbb0fSschwarze 
52ff2dbb0fSschwarze 
53ff2dbb0fSschwarze /*** top-level functions **********************************************/
54ff2dbb0fSschwarze 
55ff2dbb0fSschwarze struct dba *
dba_new(int32_t npages)56ff2dbb0fSschwarze dba_new(int32_t npages)
57ff2dbb0fSschwarze {
58ff2dbb0fSschwarze 	struct dba	*dba;
593fc2cf7eSschwarze 	struct ohash	*macro;
60ff2dbb0fSschwarze 	int32_t		 im;
61ff2dbb0fSschwarze 
62ff2dbb0fSschwarze 	dba = mandoc_malloc(sizeof(*dba));
63ff2dbb0fSschwarze 	dba->pages = dba_array_new(npages, DBA_GROW);
64ff2dbb0fSschwarze 	dba->macros = dba_array_new(MACRO_MAX, 0);
653fc2cf7eSschwarze 	for (im = 0; im < MACRO_MAX; im++) {
663fc2cf7eSschwarze 		macro = mandoc_malloc(sizeof(*macro));
673fc2cf7eSschwarze 		mandoc_ohash_init(macro, 4,
683fc2cf7eSschwarze 		    offsetof(struct macro_entry, value));
693fc2cf7eSschwarze 		dba_array_set(dba->macros, im, macro);
703fc2cf7eSschwarze 	}
71ff2dbb0fSschwarze 	return dba;
72ff2dbb0fSschwarze }
73ff2dbb0fSschwarze 
74ff2dbb0fSschwarze void
dba_free(struct dba * dba)75ff2dbb0fSschwarze dba_free(struct dba *dba)
76ff2dbb0fSschwarze {
773fc2cf7eSschwarze 	struct dba_array	*page;
783fc2cf7eSschwarze 	struct ohash		*macro;
793fc2cf7eSschwarze 	struct macro_entry	*entry;
803fc2cf7eSschwarze 	unsigned int		 slot;
81ff2dbb0fSschwarze 
82ff2dbb0fSschwarze 	dba_array_FOREACH(dba->macros, macro) {
833fc2cf7eSschwarze 		for (entry = ohash_first(macro, &slot); entry != NULL;
843fc2cf7eSschwarze 		     entry = ohash_next(macro, &slot)) {
853fc2cf7eSschwarze 			dba_array_free(entry->pages);
863fc2cf7eSschwarze 			free(entry);
87ff2dbb0fSschwarze 		}
883fc2cf7eSschwarze 		ohash_delete(macro);
893fc2cf7eSschwarze 		free(macro);
90ff2dbb0fSschwarze 	}
91ff2dbb0fSschwarze 	dba_array_free(dba->macros);
92ff2dbb0fSschwarze 
93ff2dbb0fSschwarze 	dba_array_undel(dba->pages);
94ff2dbb0fSschwarze 	dba_array_FOREACH(dba->pages, page) {
95ff2dbb0fSschwarze 		dba_array_free(dba_array_get(page, DBP_NAME));
96ff2dbb0fSschwarze 		dba_array_free(dba_array_get(page, DBP_SECT));
97ff2dbb0fSschwarze 		dba_array_free(dba_array_get(page, DBP_ARCH));
98ff2dbb0fSschwarze 		free(dba_array_get(page, DBP_DESC));
99ff2dbb0fSschwarze 		dba_array_free(dba_array_get(page, DBP_FILE));
100ff2dbb0fSschwarze 		dba_array_free(page);
101ff2dbb0fSschwarze 	}
102ff2dbb0fSschwarze 	dba_array_free(dba->pages);
103ff2dbb0fSschwarze 
104ff2dbb0fSschwarze 	free(dba);
105ff2dbb0fSschwarze }
106ff2dbb0fSschwarze 
107ff2dbb0fSschwarze /*
108ff2dbb0fSschwarze  * Write the complete mandoc database to disk; the format is:
109ff2dbb0fSschwarze  * - One integer each for magic and version.
110ff2dbb0fSschwarze  * - One pointer each to the macros table and to the final magic.
111ff2dbb0fSschwarze  * - The pages table.
112ff2dbb0fSschwarze  * - The macros table.
113ff2dbb0fSschwarze  * - And at the very end, the magic integer again.
114ff2dbb0fSschwarze  */
115ff2dbb0fSschwarze int
dba_write(const char * fname,struct dba * dba)116ff2dbb0fSschwarze dba_write(const char *fname, struct dba *dba)
117ff2dbb0fSschwarze {
118ff2dbb0fSschwarze 	int	 save_errno;
119ff2dbb0fSschwarze 	int32_t	 pos_end, pos_macros, pos_macros_ptr;
120ff2dbb0fSschwarze 
121ff2dbb0fSschwarze 	if (dba_open(fname) == -1)
122ff2dbb0fSschwarze 		return -1;
123ff2dbb0fSschwarze 	dba_int_write(MANDOCDB_MAGIC);
124ff2dbb0fSschwarze 	dba_int_write(MANDOCDB_VERSION);
125ff2dbb0fSschwarze 	pos_macros_ptr = dba_skip(1, 2);
126ff2dbb0fSschwarze 	dba_pages_write(dba->pages);
127ff2dbb0fSschwarze 	pos_macros = dba_tell();
128ff2dbb0fSschwarze 	dba_macros_write(dba->macros);
129ff2dbb0fSschwarze 	pos_end = dba_tell();
130ff2dbb0fSschwarze 	dba_int_write(MANDOCDB_MAGIC);
131ff2dbb0fSschwarze 	dba_seek(pos_macros_ptr);
132ff2dbb0fSschwarze 	dba_int_write(pos_macros);
133ff2dbb0fSschwarze 	dba_int_write(pos_end);
134ff2dbb0fSschwarze 	if (dba_close() == -1) {
135ff2dbb0fSschwarze 		save_errno = errno;
136ff2dbb0fSschwarze 		unlink(fname);
137ff2dbb0fSschwarze 		errno = save_errno;
138ff2dbb0fSschwarze 		return -1;
139ff2dbb0fSschwarze 	}
140ff2dbb0fSschwarze 	return 0;
141ff2dbb0fSschwarze }
142ff2dbb0fSschwarze 
143ff2dbb0fSschwarze 
144ff2dbb0fSschwarze /*** functions for handling pages *************************************/
145ff2dbb0fSschwarze 
146ff2dbb0fSschwarze /*
147ff2dbb0fSschwarze  * Create a new page and append it to the pages table.
148ff2dbb0fSschwarze  */
149ff2dbb0fSschwarze struct dba_array *
dba_page_new(struct dba_array * pages,const char * arch,const char * desc,const char * file,enum form form)150289fdc1aSschwarze dba_page_new(struct dba_array *pages, const char *arch,
151289fdc1aSschwarze     const char *desc, const char *file, enum form form)
152ff2dbb0fSschwarze {
153ff2dbb0fSschwarze 	struct dba_array *page, *entry;
154ff2dbb0fSschwarze 
155ff2dbb0fSschwarze 	page = dba_array_new(DBP_MAX, 0);
156ff2dbb0fSschwarze 	entry = dba_array_new(1, DBA_STR | DBA_GROW);
157ff2dbb0fSschwarze 	dba_array_add(page, entry);
158ff2dbb0fSschwarze 	entry = dba_array_new(1, DBA_STR | DBA_GROW);
159ff2dbb0fSschwarze 	dba_array_add(page, entry);
160ff2dbb0fSschwarze 	if (arch != NULL && *arch != '\0') {
161ff2dbb0fSschwarze 		entry = dba_array_new(1, DBA_STR | DBA_GROW);
162ff2dbb0fSschwarze 		dba_array_add(entry, (void *)arch);
163ff2dbb0fSschwarze 	} else
164ff2dbb0fSschwarze 		entry = NULL;
165ff2dbb0fSschwarze 	dba_array_add(page, entry);
166ff2dbb0fSschwarze 	dba_array_add(page, mandoc_strdup(desc));
167ff2dbb0fSschwarze 	entry = dba_array_new(1, DBA_STR | DBA_GROW);
168ff2dbb0fSschwarze 	dba_array_add(entry, prepend(file, form));
169ff2dbb0fSschwarze 	dba_array_add(page, entry);
170ff2dbb0fSschwarze 	dba_array_add(pages, page);
171ff2dbb0fSschwarze 	return page;
172ff2dbb0fSschwarze }
173ff2dbb0fSschwarze 
174ff2dbb0fSschwarze /*
175ff2dbb0fSschwarze  * Add a section, architecture, or file name to an existing page.
176ff2dbb0fSschwarze  * Passing the NULL pointer for the architecture makes the page MI.
177ff2dbb0fSschwarze  * In that case, any earlier or later architectures are ignored.
178ff2dbb0fSschwarze  */
179ff2dbb0fSschwarze void
dba_page_add(struct dba_array * page,int32_t ie,const char * str)180ff2dbb0fSschwarze dba_page_add(struct dba_array *page, int32_t ie, const char *str)
181ff2dbb0fSschwarze {
182ff2dbb0fSschwarze 	struct dba_array	*entries;
183ff2dbb0fSschwarze 	char			*entry;
184ff2dbb0fSschwarze 
185ff2dbb0fSschwarze 	entries = dba_array_get(page, ie);
186ff2dbb0fSschwarze 	if (ie == DBP_ARCH) {
187ff2dbb0fSschwarze 		if (entries == NULL)
188ff2dbb0fSschwarze 			return;
1890c1572bbSschwarze 		if (str == NULL || *str == '\0') {
190ff2dbb0fSschwarze 			dba_array_free(entries);
191ff2dbb0fSschwarze 			dba_array_set(page, DBP_ARCH, NULL);
192ff2dbb0fSschwarze 			return;
193ff2dbb0fSschwarze 		}
194ff2dbb0fSschwarze 	}
195ff2dbb0fSschwarze 	if (*str == '\0')
196ff2dbb0fSschwarze 		return;
197ff2dbb0fSschwarze 	dba_array_FOREACH(entries, entry) {
198ff2dbb0fSschwarze 		if (ie == DBP_FILE && *entry < ' ')
199ff2dbb0fSschwarze 			entry++;
200ff2dbb0fSschwarze 		if (strcmp(entry, str) == 0)
201ff2dbb0fSschwarze 			return;
202ff2dbb0fSschwarze 	}
203ff2dbb0fSschwarze 	dba_array_add(entries, (void *)str);
204ff2dbb0fSschwarze }
205ff2dbb0fSschwarze 
206ff2dbb0fSschwarze /*
207ff2dbb0fSschwarze  * Add an additional name to an existing page.
208ff2dbb0fSschwarze  */
209ff2dbb0fSschwarze void
dba_page_alias(struct dba_array * page,const char * name,uint64_t mask)210ff2dbb0fSschwarze dba_page_alias(struct dba_array *page, const char *name, uint64_t mask)
211ff2dbb0fSschwarze {
212ff2dbb0fSschwarze 	struct dba_array	*entries;
213ff2dbb0fSschwarze 	char			*entry;
214ff2dbb0fSschwarze 	char			 maskbyte;
215ff2dbb0fSschwarze 
216ff2dbb0fSschwarze 	if (*name == '\0')
217ff2dbb0fSschwarze 		return;
218ff2dbb0fSschwarze 	maskbyte = mask & NAME_MASK;
219ff2dbb0fSschwarze 	entries = dba_array_get(page, DBP_NAME);
220ff2dbb0fSschwarze 	dba_array_FOREACH(entries, entry) {
221ff2dbb0fSschwarze 		if (strcmp(entry + 1, name) == 0) {
222ff2dbb0fSschwarze 			*entry |= maskbyte;
223ff2dbb0fSschwarze 			return;
224ff2dbb0fSschwarze 		}
225ff2dbb0fSschwarze 	}
226ff2dbb0fSschwarze 	dba_array_add(entries, prepend(name, maskbyte));
227ff2dbb0fSschwarze }
228ff2dbb0fSschwarze 
229ff2dbb0fSschwarze /*
230ff2dbb0fSschwarze  * Return a pointer to a temporary copy of instr with inbyte prepended.
231ff2dbb0fSschwarze  */
232ff2dbb0fSschwarze static void *
prepend(const char * instr,char inbyte)233ff2dbb0fSschwarze prepend(const char *instr, char inbyte)
234ff2dbb0fSschwarze {
235ff2dbb0fSschwarze 	static char	*outstr = NULL;
236ff2dbb0fSschwarze 	static size_t	 outlen = 0;
237ff2dbb0fSschwarze 	size_t		 newlen;
238ff2dbb0fSschwarze 
239ff2dbb0fSschwarze 	newlen = strlen(instr) + 1;
240ff2dbb0fSschwarze 	if (newlen > outlen) {
241ff2dbb0fSschwarze 		outstr = mandoc_realloc(outstr, newlen + 1);
242ff2dbb0fSschwarze 		outlen = newlen;
243ff2dbb0fSschwarze 	}
244ff2dbb0fSschwarze 	*outstr = inbyte;
245ff2dbb0fSschwarze 	memcpy(outstr + 1, instr, newlen);
246ff2dbb0fSschwarze 	return outstr;
247ff2dbb0fSschwarze }
248ff2dbb0fSschwarze 
249ff2dbb0fSschwarze /*
250ff2dbb0fSschwarze  * Write the pages table to disk; the format is:
251ff2dbb0fSschwarze  * - One integer containing the number of pages.
252ff2dbb0fSschwarze  * - For each page, five pointers to the names, sections,
253ff2dbb0fSschwarze  *   architectures, description, and file names of the page.
254ff2dbb0fSschwarze  *   MI pages write 0 instead of the architecture pointer.
255ff2dbb0fSschwarze  * - One list each for names, sections, architectures, descriptions and
256ff2dbb0fSschwarze  *   file names.  The description for each page ends with a NUL byte.
257ff2dbb0fSschwarze  *   For all the other lists, each string ends with a NUL byte,
258ff2dbb0fSschwarze  *   and the last string for a page ends with two NUL bytes.
259ff2dbb0fSschwarze  * - To assure alignment of following integers,
260ff2dbb0fSschwarze  *   the end is padded with NUL bytes up to a multiple of four bytes.
261ff2dbb0fSschwarze  */
262ff2dbb0fSschwarze static void
dba_pages_write(struct dba_array * pages)263ff2dbb0fSschwarze dba_pages_write(struct dba_array *pages)
264ff2dbb0fSschwarze {
265ff2dbb0fSschwarze 	struct dba_array	*page, *entry;
266ff2dbb0fSschwarze 	int32_t			 pos_pages, pos_end;
267ff2dbb0fSschwarze 
268ff2dbb0fSschwarze 	pos_pages = dba_array_writelen(pages, 5);
269ff2dbb0fSschwarze 	dba_array_FOREACH(pages, page) {
270ff2dbb0fSschwarze 		dba_array_setpos(page, DBP_NAME, dba_tell());
271ff2dbb0fSschwarze 		entry = dba_array_get(page, DBP_NAME);
272ff2dbb0fSschwarze 		dba_array_sort(entry, compare_names);
273ff2dbb0fSschwarze 		dba_array_writelst(entry);
274ff2dbb0fSschwarze 	}
275ff2dbb0fSschwarze 	dba_array_FOREACH(pages, page) {
276ff2dbb0fSschwarze 		dba_array_setpos(page, DBP_SECT, dba_tell());
277ff2dbb0fSschwarze 		entry = dba_array_get(page, DBP_SECT);
278ff2dbb0fSschwarze 		dba_array_sort(entry, compare_strings);
279ff2dbb0fSschwarze 		dba_array_writelst(entry);
280ff2dbb0fSschwarze 	}
281ff2dbb0fSschwarze 	dba_array_FOREACH(pages, page) {
282ff2dbb0fSschwarze 		if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) {
283ff2dbb0fSschwarze 			dba_array_setpos(page, DBP_ARCH, dba_tell());
284ff2dbb0fSschwarze 			dba_array_sort(entry, compare_strings);
285ff2dbb0fSschwarze 			dba_array_writelst(entry);
286ff2dbb0fSschwarze 		} else
287ff2dbb0fSschwarze 			dba_array_setpos(page, DBP_ARCH, 0);
288ff2dbb0fSschwarze 	}
289ff2dbb0fSschwarze 	dba_array_FOREACH(pages, page) {
290ff2dbb0fSschwarze 		dba_array_setpos(page, DBP_DESC, dba_tell());
291ff2dbb0fSschwarze 		dba_str_write(dba_array_get(page, DBP_DESC));
292ff2dbb0fSschwarze 	}
293ff2dbb0fSschwarze 	dba_array_FOREACH(pages, page) {
294ff2dbb0fSschwarze 		dba_array_setpos(page, DBP_FILE, dba_tell());
295ff2dbb0fSschwarze 		dba_array_writelst(dba_array_get(page, DBP_FILE));
296ff2dbb0fSschwarze 	}
297ff2dbb0fSschwarze 	pos_end = dba_align();
298ff2dbb0fSschwarze 	dba_seek(pos_pages);
299ff2dbb0fSschwarze 	dba_array_FOREACH(pages, page)
300ff2dbb0fSschwarze 		dba_array_writepos(page);
301ff2dbb0fSschwarze 	dba_seek(pos_end);
302ff2dbb0fSschwarze }
303ff2dbb0fSschwarze 
304ff2dbb0fSschwarze static int
compare_names(const void * vp1,const void * vp2)305ff2dbb0fSschwarze compare_names(const void *vp1, const void *vp2)
306ff2dbb0fSschwarze {
307ff2dbb0fSschwarze 	const char	*cp1, *cp2;
308ff2dbb0fSschwarze 	int		 diff;
309ff2dbb0fSschwarze 
310*79a81166Sschwarze 	cp1 = *(const char * const *)vp1;
311*79a81166Sschwarze 	cp2 = *(const char * const *)vp2;
312ff2dbb0fSschwarze 	return (diff = *cp2 - *cp1) ? diff :
313ff2dbb0fSschwarze 	    strcasecmp(cp1 + 1, cp2 + 1);
314ff2dbb0fSschwarze }
315ff2dbb0fSschwarze 
316ff2dbb0fSschwarze static int
compare_strings(const void * vp1,const void * vp2)317ff2dbb0fSschwarze compare_strings(const void *vp1, const void *vp2)
318ff2dbb0fSschwarze {
319ff2dbb0fSschwarze 	const char	*cp1, *cp2;
320ff2dbb0fSschwarze 
321*79a81166Sschwarze 	cp1 = *(const char * const *)vp1;
322*79a81166Sschwarze 	cp2 = *(const char * const *)vp2;
323ff2dbb0fSschwarze 	return strcmp(cp1, cp2);
324ff2dbb0fSschwarze }
325ff2dbb0fSschwarze 
326ff2dbb0fSschwarze /*** functions for handling macros ************************************/
327ff2dbb0fSschwarze 
328ff2dbb0fSschwarze /*
3293fc2cf7eSschwarze  * In the hash table for a single macro, look up an entry by
3303fc2cf7eSschwarze  * the macro value or add an empty one if it doesn't exist yet.
3313fc2cf7eSschwarze  */
3323fc2cf7eSschwarze static struct macro_entry *
get_macro_entry(struct ohash * macro,const char * value,int32_t np)3333fc2cf7eSschwarze get_macro_entry(struct ohash *macro, const char *value, int32_t np)
3343fc2cf7eSschwarze {
3353fc2cf7eSschwarze 	struct macro_entry	*entry;
3363fc2cf7eSschwarze 	size_t			 len;
3373fc2cf7eSschwarze 	unsigned int		 slot;
3383fc2cf7eSschwarze 
3393fc2cf7eSschwarze 	slot = ohash_qlookup(macro, value);
3403fc2cf7eSschwarze 	if ((entry = ohash_find(macro, slot)) == NULL) {
3413fc2cf7eSschwarze 		len = strlen(value) + 1;
3423fc2cf7eSschwarze 		entry = mandoc_malloc(sizeof(*entry) + len);
3433fc2cf7eSschwarze 		memcpy(&entry->value, value, len);
3443fc2cf7eSschwarze 		entry->pages = dba_array_new(np, DBA_GROW);
3453fc2cf7eSschwarze 		ohash_insert(macro, slot, entry);
3463fc2cf7eSschwarze 	}
3473fc2cf7eSschwarze 	return entry;
3483fc2cf7eSschwarze }
3493fc2cf7eSschwarze 
3503fc2cf7eSschwarze /*
3513fc2cf7eSschwarze  * In addition to get_macro_entry(), add multiple page references,
3523fc2cf7eSschwarze  * converting them from the on-disk format (byte offsets in the file)
3533fc2cf7eSschwarze  * to page pointers in memory.
354ff2dbb0fSschwarze  */
355ff2dbb0fSschwarze void
dba_macro_new(struct dba * dba,int32_t im,const char * value,const int32_t * pp)356ff2dbb0fSschwarze dba_macro_new(struct dba *dba, int32_t im, const char *value,
357ff2dbb0fSschwarze     const int32_t *pp)
358ff2dbb0fSschwarze {
3593fc2cf7eSschwarze 	struct macro_entry	*entry;
360ff2dbb0fSschwarze 	const int32_t		*ip;
361ff2dbb0fSschwarze 	int32_t			 np;
362ff2dbb0fSschwarze 
363ff2dbb0fSschwarze 	np = 0;
364ff2dbb0fSschwarze 	for (ip = pp; *ip; ip++)
365ff2dbb0fSschwarze 		np++;
3663fc2cf7eSschwarze 
3673fc2cf7eSschwarze 	entry = get_macro_entry(dba_array_get(dba->macros, im), value, np);
368ff2dbb0fSschwarze 	for (ip = pp; *ip; ip++)
3693fc2cf7eSschwarze 		dba_array_add(entry->pages, dba_array_get(dba->pages,
370ff2dbb0fSschwarze 		    be32toh(*ip) / 5 / sizeof(*ip) - 1));
371ff2dbb0fSschwarze }
372ff2dbb0fSschwarze 
373ff2dbb0fSschwarze /*
3743fc2cf7eSschwarze  * In addition to get_macro_entry(), add one page reference,
3753fc2cf7eSschwarze  * directly taking the in-memory page pointer as an argument.
376ff2dbb0fSschwarze  */
377ff2dbb0fSschwarze void
dba_macro_add(struct dba_array * macros,int32_t im,const char * value,struct dba_array * page)378ff2dbb0fSschwarze dba_macro_add(struct dba_array *macros, int32_t im, const char *value,
379ff2dbb0fSschwarze     struct dba_array *page)
380ff2dbb0fSschwarze {
3813fc2cf7eSschwarze 	struct macro_entry	*entry;
382ff2dbb0fSschwarze 
383ff2dbb0fSschwarze 	if (*value == '\0')
384ff2dbb0fSschwarze 		return;
3853fc2cf7eSschwarze 	entry = get_macro_entry(dba_array_get(macros, im), value, 1);
3863fc2cf7eSschwarze 	dba_array_add(entry->pages, page);
387ff2dbb0fSschwarze }
388ff2dbb0fSschwarze 
389ff2dbb0fSschwarze /*
390ff2dbb0fSschwarze  * Write the macros table to disk; the format is:
391ff2dbb0fSschwarze  * - The number of macro tables (actually, MACRO_MAX).
392ff2dbb0fSschwarze  * - That number of pointers to the individual macro tables.
393ff2dbb0fSschwarze  * - The individual macro tables.
394ff2dbb0fSschwarze  */
395ff2dbb0fSschwarze static void
dba_macros_write(struct dba_array * macros)396ff2dbb0fSschwarze dba_macros_write(struct dba_array *macros)
397ff2dbb0fSschwarze {
3983fc2cf7eSschwarze 	struct ohash		*macro;
399ff2dbb0fSschwarze 	int32_t			 im, pos_macros, pos_end;
400ff2dbb0fSschwarze 
401ff2dbb0fSschwarze 	pos_macros = dba_array_writelen(macros, 1);
402ff2dbb0fSschwarze 	im = 0;
403ff2dbb0fSschwarze 	dba_array_FOREACH(macros, macro) {
404ff2dbb0fSschwarze 		dba_array_setpos(macros, im++, dba_tell());
405ff2dbb0fSschwarze 		dba_macro_write(macro);
406ff2dbb0fSschwarze 	}
407ff2dbb0fSschwarze 	pos_end = dba_tell();
408ff2dbb0fSschwarze 	dba_seek(pos_macros);
409ff2dbb0fSschwarze 	dba_array_writepos(macros);
410ff2dbb0fSschwarze 	dba_seek(pos_end);
411ff2dbb0fSschwarze }
412ff2dbb0fSschwarze 
413ff2dbb0fSschwarze /*
414ff2dbb0fSschwarze  * Write one individual macro table to disk; the format is:
415ff2dbb0fSschwarze  * - The number of entries in the table.
416ff2dbb0fSschwarze  * - For each entry, two pointers, the first one to the value
417ff2dbb0fSschwarze  *   and the second one to the list of pages.
418ff2dbb0fSschwarze  * - A list of values, each ending in a NUL byte.
419ff2dbb0fSschwarze  * - To assure alignment of following integers,
420ff2dbb0fSschwarze  *   padding with NUL bytes up to a multiple of four bytes.
421ff2dbb0fSschwarze  * - A list of pointers to pages, each list ending in a 0 integer.
422ff2dbb0fSschwarze  */
423ff2dbb0fSschwarze static void
dba_macro_write(struct ohash * macro)4243fc2cf7eSschwarze dba_macro_write(struct ohash *macro)
425ff2dbb0fSschwarze {
4263fc2cf7eSschwarze 	struct macro_entry	**entries, *entry;
4273fc2cf7eSschwarze 	struct dba_array	 *page;
4283fc2cf7eSschwarze 	int32_t			 *kpos, *dpos;
4293fc2cf7eSschwarze 	unsigned int		  ie, ne, slot;
4303fc2cf7eSschwarze 	int			  use;
431ff2dbb0fSschwarze 	int32_t			  addr, pos_macro, pos_end;
432ff2dbb0fSschwarze 
4333fc2cf7eSschwarze 	/* Temporary storage for filtering and sorting. */
4343fc2cf7eSschwarze 
4353fc2cf7eSschwarze 	ne = ohash_entries(macro);
4363fc2cf7eSschwarze 	entries = mandoc_reallocarray(NULL, ne, sizeof(*entries));
4373fc2cf7eSschwarze 	kpos = mandoc_reallocarray(NULL, ne, sizeof(*kpos));
4383fc2cf7eSschwarze 	dpos = mandoc_reallocarray(NULL, ne, sizeof(*dpos));
4393fc2cf7eSschwarze 
4403fc2cf7eSschwarze 	/* Build a list of non-empty entries and sort it. */
4413fc2cf7eSschwarze 
4423fc2cf7eSschwarze 	ne = 0;
4433fc2cf7eSschwarze 	for (entry = ohash_first(macro, &slot); entry != NULL;
4443fc2cf7eSschwarze 	     entry = ohash_next(macro, &slot)) {
4453fc2cf7eSschwarze 		use = 0;
4463fc2cf7eSschwarze 		dba_array_FOREACH(entry->pages, page)
447ff2dbb0fSschwarze 			if (dba_array_getpos(page))
4483fc2cf7eSschwarze 				use = 1;
4493fc2cf7eSschwarze 		if (use)
4503fc2cf7eSschwarze 			entries[ne++] = entry;
451ff2dbb0fSschwarze 	}
4523fc2cf7eSschwarze 	qsort(entries, ne, sizeof(*entries), compare_entries);
4533fc2cf7eSschwarze 
4543fc2cf7eSschwarze 	/* Number of entries, and space for the pointer pairs. */
4553fc2cf7eSschwarze 
4563fc2cf7eSschwarze 	dba_int_write(ne);
4573fc2cf7eSschwarze 	pos_macro = dba_skip(2, ne);
4583fc2cf7eSschwarze 
4593fc2cf7eSschwarze 	/* String table. */
4603fc2cf7eSschwarze 
4613fc2cf7eSschwarze 	for (ie = 0; ie < ne; ie++) {
4623fc2cf7eSschwarze 		kpos[ie] = dba_tell();
4633fc2cf7eSschwarze 		dba_str_write(entries[ie]->value);
464ff2dbb0fSschwarze 	}
465ff2dbb0fSschwarze 	dba_align();
4663fc2cf7eSschwarze 
4673fc2cf7eSschwarze 	/* Pages table. */
4683fc2cf7eSschwarze 
4693fc2cf7eSschwarze 	for (ie = 0; ie < ne; ie++) {
4703fc2cf7eSschwarze 		dpos[ie] = dba_tell();
4713fc2cf7eSschwarze 		dba_array_FOREACH(entries[ie]->pages, page)
472ff2dbb0fSschwarze 			if ((addr = dba_array_getpos(page)))
473ff2dbb0fSschwarze 				dba_int_write(addr);
474ff2dbb0fSschwarze 		dba_int_write(0);
475ff2dbb0fSschwarze 	}
476ff2dbb0fSschwarze 	pos_end = dba_tell();
4773fc2cf7eSschwarze 
4783fc2cf7eSschwarze 	/* Fill in the pointer pairs. */
4793fc2cf7eSschwarze 
480ff2dbb0fSschwarze 	dba_seek(pos_macro);
4813fc2cf7eSschwarze 	for (ie = 0; ie < ne; ie++) {
4823fc2cf7eSschwarze 		dba_int_write(kpos[ie]);
4833fc2cf7eSschwarze 		dba_int_write(dpos[ie]);
4843fc2cf7eSschwarze 	}
485ff2dbb0fSschwarze 	dba_seek(pos_end);
4863fc2cf7eSschwarze 
4873fc2cf7eSschwarze 	free(entries);
4883fc2cf7eSschwarze 	free(kpos);
4893fc2cf7eSschwarze 	free(dpos);
4903fc2cf7eSschwarze }
4913fc2cf7eSschwarze 
4923fc2cf7eSschwarze static int
compare_entries(const void * vp1,const void * vp2)4933fc2cf7eSschwarze compare_entries(const void *vp1, const void *vp2)
4943fc2cf7eSschwarze {
4953fc2cf7eSschwarze 	const struct macro_entry *ep1, *ep2;
4963fc2cf7eSschwarze 
497*79a81166Sschwarze 	ep1 = *(const struct macro_entry * const *)vp1;
498*79a81166Sschwarze 	ep2 = *(const struct macro_entry * const *)vp2;
4993fc2cf7eSschwarze 	return strcmp(ep1->value, ep2->value);
500ff2dbb0fSschwarze }
501