Home | History | Annotate | Line # | Download | only in dist
      1  1.1  christos /*	Id: dba.c,v 1.10 2017/02/17 14:43:54 schwarze Exp  */
      2  1.1  christos /*
      3  1.1  christos  * Copyright (c) 2016, 2017 Ingo Schwarze <schwarze (at) openbsd.org>
      4  1.1  christos  *
      5  1.1  christos  * Permission to use, copy, modify, and distribute this software for any
      6  1.1  christos  * purpose with or without fee is hereby granted, provided that the above
      7  1.1  christos  * copyright notice and this permission notice appear in all copies.
      8  1.1  christos  *
      9  1.1  christos  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
     10  1.1  christos  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
     11  1.1  christos  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
     12  1.1  christos  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
     13  1.1  christos  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
     14  1.1  christos  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
     15  1.1  christos  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
     16  1.1  christos  *
     17  1.1  christos  * Allocation-based version of the mandoc database, for read-write access.
     18  1.1  christos  * The interface is defined in "dba.h".
     19  1.1  christos  */
     20  1.1  christos #include "config.h"
     21  1.1  christos 
     22  1.1  christos #include <sys/types.h>
     23  1.1  christos #if HAVE_ENDIAN
     24  1.1  christos #include <endian.h>
     25  1.1  christos #elif HAVE_SYS_ENDIAN
     26  1.1  christos #include <sys/endian.h>
     27  1.1  christos #elif HAVE_NTOHL
     28  1.1  christos #include <arpa/inet.h>
     29  1.1  christos #endif
     30  1.1  christos #include <errno.h>
     31  1.1  christos #include <stddef.h>
     32  1.1  christos #include <stdint.h>
     33  1.1  christos #include <stdlib.h>
     34  1.1  christos #include <string.h>
     35  1.1  christos #include <unistd.h>
     36  1.1  christos 
     37  1.1  christos #include "mandoc_aux.h"
     38  1.1  christos #include "mandoc_ohash.h"
     39  1.1  christos #include "mansearch.h"
     40  1.1  christos #include "dba_write.h"
     41  1.1  christos #include "dba_array.h"
     42  1.1  christos #include "dba.h"
     43  1.1  christos 
     44  1.1  christos struct macro_entry {
     45  1.1  christos 	struct dba_array	*pages;
     46  1.1  christos 	char			 value[];
     47  1.1  christos };
     48  1.1  christos 
     49  1.1  christos static void	*prepend(const char *, char);
     50  1.1  christos static void	 dba_pages_write(struct dba_array *);
     51  1.1  christos static int	 compare_names(const void *, const void *);
     52  1.1  christos static int	 compare_strings(const void *, const void *);
     53  1.1  christos 
     54  1.1  christos static struct macro_entry
     55  1.1  christos 		*get_macro_entry(struct ohash *, const char *, int32_t);
     56  1.1  christos static void	 dba_macros_write(struct dba_array *);
     57  1.1  christos static void	 dba_macro_write(struct ohash *);
     58  1.1  christos static int	 compare_entries(const void *, const void *);
     59  1.1  christos 
     60  1.1  christos 
     61  1.1  christos /*** top-level functions **********************************************/
     62  1.1  christos 
     63  1.1  christos struct dba *
     64  1.1  christos dba_new(int32_t npages)
     65  1.1  christos {
     66  1.1  christos 	struct dba	*dba;
     67  1.1  christos 	struct ohash	*macro;
     68  1.1  christos 	int32_t		 im;
     69  1.1  christos 
     70  1.1  christos 	dba = mandoc_malloc(sizeof(*dba));
     71  1.1  christos 	dba->pages = dba_array_new(npages, DBA_GROW);
     72  1.1  christos 	dba->macros = dba_array_new(MACRO_MAX, 0);
     73  1.1  christos 	for (im = 0; im < MACRO_MAX; im++) {
     74  1.1  christos 		macro = mandoc_malloc(sizeof(*macro));
     75  1.1  christos 		mandoc_ohash_init(macro, 4,
     76  1.1  christos 		    offsetof(struct macro_entry, value));
     77  1.1  christos 		dba_array_set(dba->macros, im, macro);
     78  1.1  christos 	}
     79  1.1  christos 	return dba;
     80  1.1  christos }
     81  1.1  christos 
     82  1.1  christos void
     83  1.1  christos dba_free(struct dba *dba)
     84  1.1  christos {
     85  1.1  christos 	struct dba_array	*page;
     86  1.1  christos 	struct ohash		*macro;
     87  1.1  christos 	struct macro_entry	*entry;
     88  1.1  christos 	unsigned int		 slot;
     89  1.1  christos 
     90  1.1  christos 	dba_array_FOREACH(dba->macros, macro) {
     91  1.1  christos 		for (entry = ohash_first(macro, &slot); entry != NULL;
     92  1.1  christos 		     entry = ohash_next(macro, &slot)) {
     93  1.1  christos 			dba_array_free(entry->pages);
     94  1.1  christos 			free(entry);
     95  1.1  christos 		}
     96  1.1  christos 		ohash_delete(macro);
     97  1.1  christos 		free(macro);
     98  1.1  christos 	}
     99  1.1  christos 	dba_array_free(dba->macros);
    100  1.1  christos 
    101  1.1  christos 	dba_array_undel(dba->pages);
    102  1.1  christos 	dba_array_FOREACH(dba->pages, page) {
    103  1.1  christos 		dba_array_free(dba_array_get(page, DBP_NAME));
    104  1.1  christos 		dba_array_free(dba_array_get(page, DBP_SECT));
    105  1.1  christos 		dba_array_free(dba_array_get(page, DBP_ARCH));
    106  1.1  christos 		free(dba_array_get(page, DBP_DESC));
    107  1.1  christos 		dba_array_free(dba_array_get(page, DBP_FILE));
    108  1.1  christos 		dba_array_free(page);
    109  1.1  christos 	}
    110  1.1  christos 	dba_array_free(dba->pages);
    111  1.1  christos 
    112  1.1  christos 	free(dba);
    113  1.1  christos }
    114  1.1  christos 
    115  1.1  christos /*
    116  1.1  christos  * Write the complete mandoc database to disk; the format is:
    117  1.1  christos  * - One integer each for magic and version.
    118  1.1  christos  * - One pointer each to the macros table and to the final magic.
    119  1.1  christos  * - The pages table.
    120  1.1  christos  * - The macros table.
    121  1.1  christos  * - And at the very end, the magic integer again.
    122  1.1  christos  */
    123  1.1  christos int
    124  1.1  christos dba_write(const char *fname, struct dba *dba)
    125  1.1  christos {
    126  1.1  christos 	int	 save_errno;
    127  1.1  christos 	int32_t	 pos_end, pos_macros, pos_macros_ptr;
    128  1.1  christos 
    129  1.1  christos 	if (dba_open(fname) == -1)
    130  1.1  christos 		return -1;
    131  1.1  christos 	dba_int_write(MANDOCDB_MAGIC);
    132  1.1  christos 	dba_int_write(MANDOCDB_VERSION);
    133  1.1  christos 	pos_macros_ptr = dba_skip(1, 2);
    134  1.1  christos 	dba_pages_write(dba->pages);
    135  1.1  christos 	pos_macros = dba_tell();
    136  1.1  christos 	dba_macros_write(dba->macros);
    137  1.1  christos 	pos_end = dba_tell();
    138  1.1  christos 	dba_int_write(MANDOCDB_MAGIC);
    139  1.1  christos 	dba_seek(pos_macros_ptr);
    140  1.1  christos 	dba_int_write(pos_macros);
    141  1.1  christos 	dba_int_write(pos_end);
    142  1.1  christos 	if (dba_close() == -1) {
    143  1.1  christos 		save_errno = errno;
    144  1.1  christos 		unlink(fname);
    145  1.1  christos 		errno = save_errno;
    146  1.1  christos 		return -1;
    147  1.1  christos 	}
    148  1.1  christos 	return 0;
    149  1.1  christos }
    150  1.1  christos 
    151  1.1  christos 
    152  1.1  christos /*** functions for handling pages *************************************/
    153  1.1  christos 
    154  1.1  christos /*
    155  1.1  christos  * Create a new page and append it to the pages table.
    156  1.1  christos  */
    157  1.1  christos struct dba_array *
    158  1.1  christos dba_page_new(struct dba_array *pages, const char *arch,
    159  1.1  christos     const char *desc, const char *file, enum form form)
    160  1.1  christos {
    161  1.1  christos 	struct dba_array *page, *entry;
    162  1.1  christos 
    163  1.1  christos 	page = dba_array_new(DBP_MAX, 0);
    164  1.1  christos 	entry = dba_array_new(1, DBA_STR | DBA_GROW);
    165  1.1  christos 	dba_array_add(page, entry);
    166  1.1  christos 	entry = dba_array_new(1, DBA_STR | DBA_GROW);
    167  1.1  christos 	dba_array_add(page, entry);
    168  1.1  christos 	if (arch != NULL && *arch != '\0') {
    169  1.1  christos 		entry = dba_array_new(1, DBA_STR | DBA_GROW);
    170  1.2  christos 		dba_array_add(entry, __UNCONST(arch));
    171  1.1  christos 	} else
    172  1.1  christos 		entry = NULL;
    173  1.1  christos 	dba_array_add(page, entry);
    174  1.1  christos 	dba_array_add(page, mandoc_strdup(desc));
    175  1.1  christos 	entry = dba_array_new(1, DBA_STR | DBA_GROW);
    176  1.1  christos 	dba_array_add(entry, prepend(file, form));
    177  1.1  christos 	dba_array_add(page, entry);
    178  1.1  christos 	dba_array_add(pages, page);
    179  1.1  christos 	return page;
    180  1.1  christos }
    181  1.1  christos 
    182  1.1  christos /*
    183  1.1  christos  * Add a section, architecture, or file name to an existing page.
    184  1.1  christos  * Passing the NULL pointer for the architecture makes the page MI.
    185  1.1  christos  * In that case, any earlier or later architectures are ignored.
    186  1.1  christos  */
    187  1.1  christos void
    188  1.1  christos dba_page_add(struct dba_array *page, int32_t ie, const char *str)
    189  1.1  christos {
    190  1.1  christos 	struct dba_array	*entries;
    191  1.1  christos 	char			*entry;
    192  1.1  christos 
    193  1.1  christos 	entries = dba_array_get(page, ie);
    194  1.1  christos 	if (ie == DBP_ARCH) {
    195  1.1  christos 		if (entries == NULL)
    196  1.1  christos 			return;
    197  1.1  christos 		if (str == NULL || *str == '\0') {
    198  1.1  christos 			dba_array_free(entries);
    199  1.1  christos 			dba_array_set(page, DBP_ARCH, NULL);
    200  1.1  christos 			return;
    201  1.1  christos 		}
    202  1.1  christos 	}
    203  1.1  christos 	if (*str == '\0')
    204  1.1  christos 		return;
    205  1.1  christos 	dba_array_FOREACH(entries, entry) {
    206  1.1  christos 		if (ie == DBP_FILE && *entry < ' ')
    207  1.1  christos 			entry++;
    208  1.1  christos 		if (strcmp(entry, str) == 0)
    209  1.1  christos 			return;
    210  1.1  christos 	}
    211  1.2  christos 	dba_array_add(entries, __UNCONST(str));
    212  1.1  christos }
    213  1.1  christos 
    214  1.1  christos /*
    215  1.1  christos  * Add an additional name to an existing page.
    216  1.1  christos  */
    217  1.1  christos void
    218  1.1  christos dba_page_alias(struct dba_array *page, const char *name, uint64_t mask)
    219  1.1  christos {
    220  1.1  christos 	struct dba_array	*entries;
    221  1.1  christos 	char			*entry;
    222  1.1  christos 	char			 maskbyte;
    223  1.1  christos 
    224  1.1  christos 	if (*name == '\0')
    225  1.1  christos 		return;
    226  1.1  christos 	maskbyte = mask & NAME_MASK;
    227  1.1  christos 	entries = dba_array_get(page, DBP_NAME);
    228  1.1  christos 	dba_array_FOREACH(entries, entry) {
    229  1.1  christos 		if (strcmp(entry + 1, name) == 0) {
    230  1.1  christos 			*entry |= maskbyte;
    231  1.1  christos 			return;
    232  1.1  christos 		}
    233  1.1  christos 	}
    234  1.1  christos 	dba_array_add(entries, prepend(name, maskbyte));
    235  1.1  christos }
    236  1.1  christos 
    237  1.1  christos /*
    238  1.1  christos  * Return a pointer to a temporary copy of instr with inbyte prepended.
    239  1.1  christos  */
    240  1.1  christos static void *
    241  1.1  christos prepend(const char *instr, char inbyte)
    242  1.1  christos {
    243  1.1  christos 	static char	*outstr = NULL;
    244  1.1  christos 	static size_t	 outlen = 0;
    245  1.1  christos 	size_t		 newlen;
    246  1.1  christos 
    247  1.1  christos 	newlen = strlen(instr) + 1;
    248  1.1  christos 	if (newlen > outlen) {
    249  1.1  christos 		outstr = mandoc_realloc(outstr, newlen + 1);
    250  1.1  christos 		outlen = newlen;
    251  1.1  christos 	}
    252  1.1  christos 	*outstr = inbyte;
    253  1.1  christos 	memcpy(outstr + 1, instr, newlen);
    254  1.1  christos 	return outstr;
    255  1.1  christos }
    256  1.1  christos 
    257  1.1  christos /*
    258  1.1  christos  * Write the pages table to disk; the format is:
    259  1.1  christos  * - One integer containing the number of pages.
    260  1.1  christos  * - For each page, five pointers to the names, sections,
    261  1.1  christos  *   architectures, description, and file names of the page.
    262  1.1  christos  *   MI pages write 0 instead of the architecture pointer.
    263  1.1  christos  * - One list each for names, sections, architectures, descriptions and
    264  1.1  christos  *   file names.  The description for each page ends with a NUL byte.
    265  1.1  christos  *   For all the other lists, each string ends with a NUL byte,
    266  1.1  christos  *   and the last string for a page ends with two NUL bytes.
    267  1.1  christos  * - To assure alignment of following integers,
    268  1.1  christos  *   the end is padded with NUL bytes up to a multiple of four bytes.
    269  1.1  christos  */
    270  1.1  christos static void
    271  1.1  christos dba_pages_write(struct dba_array *pages)
    272  1.1  christos {
    273  1.1  christos 	struct dba_array	*page, *entry;
    274  1.1  christos 	int32_t			 pos_pages, pos_end;
    275  1.1  christos 
    276  1.1  christos 	pos_pages = dba_array_writelen(pages, 5);
    277  1.1  christos 	dba_array_FOREACH(pages, page) {
    278  1.1  christos 		dba_array_setpos(page, DBP_NAME, dba_tell());
    279  1.1  christos 		entry = dba_array_get(page, DBP_NAME);
    280  1.1  christos 		dba_array_sort(entry, compare_names);
    281  1.1  christos 		dba_array_writelst(entry);
    282  1.1  christos 	}
    283  1.1  christos 	dba_array_FOREACH(pages, page) {
    284  1.1  christos 		dba_array_setpos(page, DBP_SECT, dba_tell());
    285  1.1  christos 		entry = dba_array_get(page, DBP_SECT);
    286  1.1  christos 		dba_array_sort(entry, compare_strings);
    287  1.1  christos 		dba_array_writelst(entry);
    288  1.1  christos 	}
    289  1.1  christos 	dba_array_FOREACH(pages, page) {
    290  1.1  christos 		if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) {
    291  1.1  christos 			dba_array_setpos(page, DBP_ARCH, dba_tell());
    292  1.1  christos 			dba_array_sort(entry, compare_strings);
    293  1.1  christos 			dba_array_writelst(entry);
    294  1.1  christos 		} else
    295  1.1  christos 			dba_array_setpos(page, DBP_ARCH, 0);
    296  1.1  christos 	}
    297  1.1  christos 	dba_array_FOREACH(pages, page) {
    298  1.1  christos 		dba_array_setpos(page, DBP_DESC, dba_tell());
    299  1.1  christos 		dba_str_write(dba_array_get(page, DBP_DESC));
    300  1.1  christos 	}
    301  1.1  christos 	dba_array_FOREACH(pages, page) {
    302  1.1  christos 		dba_array_setpos(page, DBP_FILE, dba_tell());
    303  1.1  christos 		dba_array_writelst(dba_array_get(page, DBP_FILE));
    304  1.1  christos 	}
    305  1.1  christos 	pos_end = dba_align();
    306  1.1  christos 	dba_seek(pos_pages);
    307  1.1  christos 	dba_array_FOREACH(pages, page)
    308  1.1  christos 		dba_array_writepos(page);
    309  1.1  christos 	dba_seek(pos_end);
    310  1.1  christos }
    311  1.1  christos 
    312  1.1  christos static int
    313  1.1  christos compare_names(const void *vp1, const void *vp2)
    314  1.1  christos {
    315  1.1  christos 	const char	*cp1, *cp2;
    316  1.1  christos 	int		 diff;
    317  1.1  christos 
    318  1.1  christos 	cp1 = *(const char * const *)vp1;
    319  1.1  christos 	cp2 = *(const char * const *)vp2;
    320  1.1  christos 	return (diff = *cp2 - *cp1) ? diff :
    321  1.1  christos 	    strcasecmp(cp1 + 1, cp2 + 1);
    322  1.1  christos }
    323  1.1  christos 
    324  1.1  christos static int
    325  1.1  christos compare_strings(const void *vp1, const void *vp2)
    326  1.1  christos {
    327  1.1  christos 	const char	*cp1, *cp2;
    328  1.1  christos 
    329  1.1  christos 	cp1 = *(const char * const *)vp1;
    330  1.1  christos 	cp2 = *(const char * const *)vp2;
    331  1.1  christos 	return strcmp(cp1, cp2);
    332  1.1  christos }
    333  1.1  christos 
    334  1.1  christos /*** functions for handling macros ************************************/
    335  1.1  christos 
    336  1.1  christos /*
    337  1.1  christos  * In the hash table for a single macro, look up an entry by
    338  1.1  christos  * the macro value or add an empty one if it doesn't exist yet.
    339  1.1  christos  */
    340  1.1  christos static struct macro_entry *
    341  1.1  christos get_macro_entry(struct ohash *macro, const char *value, int32_t np)
    342  1.1  christos {
    343  1.1  christos 	struct macro_entry	*entry;
    344  1.1  christos 	size_t			 len;
    345  1.1  christos 	unsigned int		 slot;
    346  1.1  christos 
    347  1.1  christos 	slot = ohash_qlookup(macro, value);
    348  1.1  christos 	if ((entry = ohash_find(macro, slot)) == NULL) {
    349  1.1  christos 		len = strlen(value) + 1;
    350  1.1  christos 		entry = mandoc_malloc(sizeof(*entry) + len);
    351  1.1  christos 		memcpy(&entry->value, value, len);
    352  1.1  christos 		entry->pages = dba_array_new(np, DBA_GROW);
    353  1.1  christos 		ohash_insert(macro, slot, entry);
    354  1.1  christos 	}
    355  1.1  christos 	return entry;
    356  1.1  christos }
    357  1.1  christos 
    358  1.1  christos /*
    359  1.1  christos  * In addition to get_macro_entry(), add multiple page references,
    360  1.1  christos  * converting them from the on-disk format (byte offsets in the file)
    361  1.1  christos  * to page pointers in memory.
    362  1.1  christos  */
    363  1.1  christos void
    364  1.1  christos dba_macro_new(struct dba *dba, int32_t im, const char *value,
    365  1.1  christos     const int32_t *pp)
    366  1.1  christos {
    367  1.1  christos 	struct macro_entry	*entry;
    368  1.1  christos 	const int32_t		*ip;
    369  1.1  christos 	int32_t			 np;
    370  1.1  christos 
    371  1.1  christos 	np = 0;
    372  1.1  christos 	for (ip = pp; *ip; ip++)
    373  1.1  christos 		np++;
    374  1.1  christos 
    375  1.1  christos 	entry = get_macro_entry(dba_array_get(dba->macros, im), value, np);
    376  1.1  christos 	for (ip = pp; *ip; ip++)
    377  1.1  christos 		dba_array_add(entry->pages, dba_array_get(dba->pages,
    378  1.1  christos 		    be32toh(*ip) / 5 / sizeof(*ip) - 1));
    379  1.1  christos }
    380  1.1  christos 
    381  1.1  christos /*
    382  1.1  christos  * In addition to get_macro_entry(), add one page reference,
    383  1.1  christos  * directly taking the in-memory page pointer as an argument.
    384  1.1  christos  */
    385  1.1  christos void
    386  1.1  christos dba_macro_add(struct dba_array *macros, int32_t im, const char *value,
    387  1.1  christos     struct dba_array *page)
    388  1.1  christos {
    389  1.1  christos 	struct macro_entry	*entry;
    390  1.1  christos 
    391  1.1  christos 	if (*value == '\0')
    392  1.1  christos 		return;
    393  1.1  christos 	entry = get_macro_entry(dba_array_get(macros, im), value, 1);
    394  1.1  christos 	dba_array_add(entry->pages, page);
    395  1.1  christos }
    396  1.1  christos 
    397  1.1  christos /*
    398  1.1  christos  * Write the macros table to disk; the format is:
    399  1.1  christos  * - The number of macro tables (actually, MACRO_MAX).
    400  1.1  christos  * - That number of pointers to the individual macro tables.
    401  1.1  christos  * - The individual macro tables.
    402  1.1  christos  */
    403  1.1  christos static void
    404  1.1  christos dba_macros_write(struct dba_array *macros)
    405  1.1  christos {
    406  1.1  christos 	struct ohash		*macro;
    407  1.1  christos 	int32_t			 im, pos_macros, pos_end;
    408  1.1  christos 
    409  1.1  christos 	pos_macros = dba_array_writelen(macros, 1);
    410  1.1  christos 	im = 0;
    411  1.1  christos 	dba_array_FOREACH(macros, macro) {
    412  1.1  christos 		dba_array_setpos(macros, im++, dba_tell());
    413  1.1  christos 		dba_macro_write(macro);
    414  1.1  christos 	}
    415  1.1  christos 	pos_end = dba_tell();
    416  1.1  christos 	dba_seek(pos_macros);
    417  1.1  christos 	dba_array_writepos(macros);
    418  1.1  christos 	dba_seek(pos_end);
    419  1.1  christos }
    420  1.1  christos 
    421  1.1  christos /*
    422  1.1  christos  * Write one individual macro table to disk; the format is:
    423  1.1  christos  * - The number of entries in the table.
    424  1.1  christos  * - For each entry, two pointers, the first one to the value
    425  1.1  christos  *   and the second one to the list of pages.
    426  1.1  christos  * - A list of values, each ending in a NUL byte.
    427  1.1  christos  * - To assure alignment of following integers,
    428  1.1  christos  *   padding with NUL bytes up to a multiple of four bytes.
    429  1.1  christos  * - A list of pointers to pages, each list ending in a 0 integer.
    430  1.1  christos  */
    431  1.1  christos static void
    432  1.1  christos dba_macro_write(struct ohash *macro)
    433  1.1  christos {
    434  1.1  christos 	struct macro_entry	**entries, *entry;
    435  1.1  christos 	struct dba_array	 *page;
    436  1.1  christos 	int32_t			 *kpos, *dpos;
    437  1.1  christos 	unsigned int		  ie, ne, slot;
    438  1.1  christos 	int			  use;
    439  1.1  christos 	int32_t			  addr, pos_macro, pos_end;
    440  1.1  christos 
    441  1.1  christos 	/* Temporary storage for filtering and sorting. */
    442  1.1  christos 
    443  1.1  christos 	ne = ohash_entries(macro);
    444  1.1  christos 	entries = mandoc_reallocarray(NULL, ne, sizeof(*entries));
    445  1.1  christos 	kpos = mandoc_reallocarray(NULL, ne, sizeof(*kpos));
    446  1.1  christos 	dpos = mandoc_reallocarray(NULL, ne, sizeof(*dpos));
    447  1.1  christos 
    448  1.1  christos 	/* Build a list of non-empty entries and sort it. */
    449  1.1  christos 
    450  1.1  christos 	ne = 0;
    451  1.1  christos 	for (entry = ohash_first(macro, &slot); entry != NULL;
    452  1.1  christos 	     entry = ohash_next(macro, &slot)) {
    453  1.1  christos 		use = 0;
    454  1.1  christos 		dba_array_FOREACH(entry->pages, page)
    455  1.1  christos 			if (dba_array_getpos(page))
    456  1.1  christos 				use = 1;
    457  1.1  christos 		if (use)
    458  1.1  christos 			entries[ne++] = entry;
    459  1.1  christos 	}
    460  1.1  christos 	qsort(entries, ne, sizeof(*entries), compare_entries);
    461  1.1  christos 
    462  1.1  christos 	/* Number of entries, and space for the pointer pairs. */
    463  1.1  christos 
    464  1.1  christos 	dba_int_write(ne);
    465  1.1  christos 	pos_macro = dba_skip(2, ne);
    466  1.1  christos 
    467  1.1  christos 	/* String table. */
    468  1.1  christos 
    469  1.1  christos 	for (ie = 0; ie < ne; ie++) {
    470  1.1  christos 		kpos[ie] = dba_tell();
    471  1.1  christos 		dba_str_write(entries[ie]->value);
    472  1.1  christos 	}
    473  1.1  christos 	dba_align();
    474  1.1  christos 
    475  1.1  christos 	/* Pages table. */
    476  1.1  christos 
    477  1.1  christos 	for (ie = 0; ie < ne; ie++) {
    478  1.1  christos 		dpos[ie] = dba_tell();
    479  1.1  christos 		dba_array_FOREACH(entries[ie]->pages, page)
    480  1.1  christos 			if ((addr = dba_array_getpos(page)))
    481  1.1  christos 				dba_int_write(addr);
    482  1.1  christos 		dba_int_write(0);
    483  1.1  christos 	}
    484  1.1  christos 	pos_end = dba_tell();
    485  1.1  christos 
    486  1.1  christos 	/* Fill in the pointer pairs. */
    487  1.1  christos 
    488  1.1  christos 	dba_seek(pos_macro);
    489  1.1  christos 	for (ie = 0; ie < ne; ie++) {
    490  1.1  christos 		dba_int_write(kpos[ie]);
    491  1.1  christos 		dba_int_write(dpos[ie]);
    492  1.1  christos 	}
    493  1.1  christos 	dba_seek(pos_end);
    494  1.1  christos 
    495  1.1  christos 	free(entries);
    496  1.1  christos 	free(kpos);
    497  1.1  christos 	free(dpos);
    498  1.1  christos }
    499  1.1  christos 
    500  1.1  christos static int
    501  1.1  christos compare_entries(const void *vp1, const void *vp2)
    502  1.1  christos {
    503  1.1  christos 	const struct macro_entry *ep1, *ep2;
    504  1.1  christos 
    505  1.1  christos 	ep1 = *(const struct macro_entry * const *)vp1;
    506  1.1  christos 	ep2 = *(const struct macro_entry * const *)vp2;
    507  1.1  christos 	return strcmp(ep1->value, ep2->value);
    508  1.1  christos }
    509