19508192eSchristos /* Id: dba.c,v 1.10 2017/02/17 14:43:54 schwarze Exp */
29508192eSchristos /*
39508192eSchristos * Copyright (c) 2016, 2017 Ingo Schwarze <schwarze@openbsd.org>
49508192eSchristos *
59508192eSchristos * Permission to use, copy, modify, and distribute this software for any
69508192eSchristos * purpose with or without fee is hereby granted, provided that the above
79508192eSchristos * copyright notice and this permission notice appear in all copies.
89508192eSchristos *
99508192eSchristos * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
109508192eSchristos * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
119508192eSchristos * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
129508192eSchristos * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
139508192eSchristos * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
149508192eSchristos * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
159508192eSchristos * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
169508192eSchristos *
179508192eSchristos * Allocation-based version of the mandoc database, for read-write access.
189508192eSchristos * The interface is defined in "dba.h".
199508192eSchristos */
209508192eSchristos #include "config.h"
219508192eSchristos
229508192eSchristos #include <sys/types.h>
239508192eSchristos #if HAVE_ENDIAN
249508192eSchristos #include <endian.h>
259508192eSchristos #elif HAVE_SYS_ENDIAN
269508192eSchristos #include <sys/endian.h>
279508192eSchristos #elif HAVE_NTOHL
289508192eSchristos #include <arpa/inet.h>
299508192eSchristos #endif
309508192eSchristos #include <errno.h>
319508192eSchristos #include <stddef.h>
329508192eSchristos #include <stdint.h>
339508192eSchristos #include <stdlib.h>
349508192eSchristos #include <string.h>
359508192eSchristos #include <unistd.h>
369508192eSchristos
379508192eSchristos #include "mandoc_aux.h"
389508192eSchristos #include "mandoc_ohash.h"
399508192eSchristos #include "mansearch.h"
409508192eSchristos #include "dba_write.h"
419508192eSchristos #include "dba_array.h"
429508192eSchristos #include "dba.h"
439508192eSchristos
449508192eSchristos struct macro_entry {
459508192eSchristos struct dba_array *pages;
469508192eSchristos char value[];
479508192eSchristos };
489508192eSchristos
499508192eSchristos static void *prepend(const char *, char);
509508192eSchristos static void dba_pages_write(struct dba_array *);
519508192eSchristos static int compare_names(const void *, const void *);
529508192eSchristos static int compare_strings(const void *, const void *);
539508192eSchristos
549508192eSchristos static struct macro_entry
559508192eSchristos *get_macro_entry(struct ohash *, const char *, int32_t);
569508192eSchristos static void dba_macros_write(struct dba_array *);
579508192eSchristos static void dba_macro_write(struct ohash *);
589508192eSchristos static int compare_entries(const void *, const void *);
599508192eSchristos
609508192eSchristos
619508192eSchristos /*** top-level functions **********************************************/
629508192eSchristos
639508192eSchristos struct dba *
dba_new(int32_t npages)649508192eSchristos dba_new(int32_t npages)
659508192eSchristos {
669508192eSchristos struct dba *dba;
679508192eSchristos struct ohash *macro;
689508192eSchristos int32_t im;
699508192eSchristos
709508192eSchristos dba = mandoc_malloc(sizeof(*dba));
719508192eSchristos dba->pages = dba_array_new(npages, DBA_GROW);
729508192eSchristos dba->macros = dba_array_new(MACRO_MAX, 0);
739508192eSchristos for (im = 0; im < MACRO_MAX; im++) {
749508192eSchristos macro = mandoc_malloc(sizeof(*macro));
759508192eSchristos mandoc_ohash_init(macro, 4,
769508192eSchristos offsetof(struct macro_entry, value));
779508192eSchristos dba_array_set(dba->macros, im, macro);
789508192eSchristos }
799508192eSchristos return dba;
809508192eSchristos }
819508192eSchristos
829508192eSchristos void
dba_free(struct dba * dba)839508192eSchristos dba_free(struct dba *dba)
849508192eSchristos {
859508192eSchristos struct dba_array *page;
869508192eSchristos struct ohash *macro;
879508192eSchristos struct macro_entry *entry;
889508192eSchristos unsigned int slot;
899508192eSchristos
909508192eSchristos dba_array_FOREACH(dba->macros, macro) {
919508192eSchristos for (entry = ohash_first(macro, &slot); entry != NULL;
929508192eSchristos entry = ohash_next(macro, &slot)) {
939508192eSchristos dba_array_free(entry->pages);
949508192eSchristos free(entry);
959508192eSchristos }
969508192eSchristos ohash_delete(macro);
979508192eSchristos free(macro);
989508192eSchristos }
999508192eSchristos dba_array_free(dba->macros);
1009508192eSchristos
1019508192eSchristos dba_array_undel(dba->pages);
1029508192eSchristos dba_array_FOREACH(dba->pages, page) {
1039508192eSchristos dba_array_free(dba_array_get(page, DBP_NAME));
1049508192eSchristos dba_array_free(dba_array_get(page, DBP_SECT));
1059508192eSchristos dba_array_free(dba_array_get(page, DBP_ARCH));
1069508192eSchristos free(dba_array_get(page, DBP_DESC));
1079508192eSchristos dba_array_free(dba_array_get(page, DBP_FILE));
1089508192eSchristos dba_array_free(page);
1099508192eSchristos }
1109508192eSchristos dba_array_free(dba->pages);
1119508192eSchristos
1129508192eSchristos free(dba);
1139508192eSchristos }
1149508192eSchristos
1159508192eSchristos /*
1169508192eSchristos * Write the complete mandoc database to disk; the format is:
1179508192eSchristos * - One integer each for magic and version.
1189508192eSchristos * - One pointer each to the macros table and to the final magic.
1199508192eSchristos * - The pages table.
1209508192eSchristos * - The macros table.
1219508192eSchristos * - And at the very end, the magic integer again.
1229508192eSchristos */
1239508192eSchristos int
dba_write(const char * fname,struct dba * dba)1249508192eSchristos dba_write(const char *fname, struct dba *dba)
1259508192eSchristos {
1269508192eSchristos int save_errno;
1279508192eSchristos int32_t pos_end, pos_macros, pos_macros_ptr;
1289508192eSchristos
1299508192eSchristos if (dba_open(fname) == -1)
1309508192eSchristos return -1;
1319508192eSchristos dba_int_write(MANDOCDB_MAGIC);
1329508192eSchristos dba_int_write(MANDOCDB_VERSION);
1339508192eSchristos pos_macros_ptr = dba_skip(1, 2);
1349508192eSchristos dba_pages_write(dba->pages);
1359508192eSchristos pos_macros = dba_tell();
1369508192eSchristos dba_macros_write(dba->macros);
1379508192eSchristos pos_end = dba_tell();
1389508192eSchristos dba_int_write(MANDOCDB_MAGIC);
1399508192eSchristos dba_seek(pos_macros_ptr);
1409508192eSchristos dba_int_write(pos_macros);
1419508192eSchristos dba_int_write(pos_end);
1429508192eSchristos if (dba_close() == -1) {
1439508192eSchristos save_errno = errno;
1449508192eSchristos unlink(fname);
1459508192eSchristos errno = save_errno;
1469508192eSchristos return -1;
1479508192eSchristos }
1489508192eSchristos return 0;
1499508192eSchristos }
1509508192eSchristos
1519508192eSchristos
1529508192eSchristos /*** functions for handling pages *************************************/
1539508192eSchristos
1549508192eSchristos /*
1559508192eSchristos * Create a new page and append it to the pages table.
1569508192eSchristos */
1579508192eSchristos struct dba_array *
dba_page_new(struct dba_array * pages,const char * arch,const char * desc,const char * file,enum form form)1589508192eSchristos dba_page_new(struct dba_array *pages, const char *arch,
1599508192eSchristos const char *desc, const char *file, enum form form)
1609508192eSchristos {
1619508192eSchristos struct dba_array *page, *entry;
1629508192eSchristos
1639508192eSchristos page = dba_array_new(DBP_MAX, 0);
1649508192eSchristos entry = dba_array_new(1, DBA_STR | DBA_GROW);
1659508192eSchristos dba_array_add(page, entry);
1669508192eSchristos entry = dba_array_new(1, DBA_STR | DBA_GROW);
1679508192eSchristos dba_array_add(page, entry);
1689508192eSchristos if (arch != NULL && *arch != '\0') {
1699508192eSchristos entry = dba_array_new(1, DBA_STR | DBA_GROW);
170*3cae1599Schristos dba_array_add(entry, __UNCONST(arch));
1719508192eSchristos } else
1729508192eSchristos entry = NULL;
1739508192eSchristos dba_array_add(page, entry);
1749508192eSchristos dba_array_add(page, mandoc_strdup(desc));
1759508192eSchristos entry = dba_array_new(1, DBA_STR | DBA_GROW);
1769508192eSchristos dba_array_add(entry, prepend(file, form));
1779508192eSchristos dba_array_add(page, entry);
1789508192eSchristos dba_array_add(pages, page);
1799508192eSchristos return page;
1809508192eSchristos }
1819508192eSchristos
1829508192eSchristos /*
1839508192eSchristos * Add a section, architecture, or file name to an existing page.
1849508192eSchristos * Passing the NULL pointer for the architecture makes the page MI.
1859508192eSchristos * In that case, any earlier or later architectures are ignored.
1869508192eSchristos */
1879508192eSchristos void
dba_page_add(struct dba_array * page,int32_t ie,const char * str)1889508192eSchristos dba_page_add(struct dba_array *page, int32_t ie, const char *str)
1899508192eSchristos {
1909508192eSchristos struct dba_array *entries;
1919508192eSchristos char *entry;
1929508192eSchristos
1939508192eSchristos entries = dba_array_get(page, ie);
1949508192eSchristos if (ie == DBP_ARCH) {
1959508192eSchristos if (entries == NULL)
1969508192eSchristos return;
1979508192eSchristos if (str == NULL || *str == '\0') {
1989508192eSchristos dba_array_free(entries);
1999508192eSchristos dba_array_set(page, DBP_ARCH, NULL);
2009508192eSchristos return;
2019508192eSchristos }
2029508192eSchristos }
2039508192eSchristos if (*str == '\0')
2049508192eSchristos return;
2059508192eSchristos dba_array_FOREACH(entries, entry) {
2069508192eSchristos if (ie == DBP_FILE && *entry < ' ')
2079508192eSchristos entry++;
2089508192eSchristos if (strcmp(entry, str) == 0)
2099508192eSchristos return;
2109508192eSchristos }
211*3cae1599Schristos dba_array_add(entries, __UNCONST(str));
2129508192eSchristos }
2139508192eSchristos
2149508192eSchristos /*
2159508192eSchristos * Add an additional name to an existing page.
2169508192eSchristos */
2179508192eSchristos void
dba_page_alias(struct dba_array * page,const char * name,uint64_t mask)2189508192eSchristos dba_page_alias(struct dba_array *page, const char *name, uint64_t mask)
2199508192eSchristos {
2209508192eSchristos struct dba_array *entries;
2219508192eSchristos char *entry;
2229508192eSchristos char maskbyte;
2239508192eSchristos
2249508192eSchristos if (*name == '\0')
2259508192eSchristos return;
2269508192eSchristos maskbyte = mask & NAME_MASK;
2279508192eSchristos entries = dba_array_get(page, DBP_NAME);
2289508192eSchristos dba_array_FOREACH(entries, entry) {
2299508192eSchristos if (strcmp(entry + 1, name) == 0) {
2309508192eSchristos *entry |= maskbyte;
2319508192eSchristos return;
2329508192eSchristos }
2339508192eSchristos }
2349508192eSchristos dba_array_add(entries, prepend(name, maskbyte));
2359508192eSchristos }
2369508192eSchristos
2379508192eSchristos /*
2389508192eSchristos * Return a pointer to a temporary copy of instr with inbyte prepended.
2399508192eSchristos */
2409508192eSchristos static void *
prepend(const char * instr,char inbyte)2419508192eSchristos prepend(const char *instr, char inbyte)
2429508192eSchristos {
2439508192eSchristos static char *outstr = NULL;
2449508192eSchristos static size_t outlen = 0;
2459508192eSchristos size_t newlen;
2469508192eSchristos
2479508192eSchristos newlen = strlen(instr) + 1;
2489508192eSchristos if (newlen > outlen) {
2499508192eSchristos outstr = mandoc_realloc(outstr, newlen + 1);
2509508192eSchristos outlen = newlen;
2519508192eSchristos }
2529508192eSchristos *outstr = inbyte;
2539508192eSchristos memcpy(outstr + 1, instr, newlen);
2549508192eSchristos return outstr;
2559508192eSchristos }
2569508192eSchristos
2579508192eSchristos /*
2589508192eSchristos * Write the pages table to disk; the format is:
2599508192eSchristos * - One integer containing the number of pages.
2609508192eSchristos * - For each page, five pointers to the names, sections,
2619508192eSchristos * architectures, description, and file names of the page.
2629508192eSchristos * MI pages write 0 instead of the architecture pointer.
2639508192eSchristos * - One list each for names, sections, architectures, descriptions and
2649508192eSchristos * file names. The description for each page ends with a NUL byte.
2659508192eSchristos * For all the other lists, each string ends with a NUL byte,
2669508192eSchristos * and the last string for a page ends with two NUL bytes.
2679508192eSchristos * - To assure alignment of following integers,
2689508192eSchristos * the end is padded with NUL bytes up to a multiple of four bytes.
2699508192eSchristos */
2709508192eSchristos static void
dba_pages_write(struct dba_array * pages)2719508192eSchristos dba_pages_write(struct dba_array *pages)
2729508192eSchristos {
2739508192eSchristos struct dba_array *page, *entry;
2749508192eSchristos int32_t pos_pages, pos_end;
2759508192eSchristos
2769508192eSchristos pos_pages = dba_array_writelen(pages, 5);
2779508192eSchristos dba_array_FOREACH(pages, page) {
2789508192eSchristos dba_array_setpos(page, DBP_NAME, dba_tell());
2799508192eSchristos entry = dba_array_get(page, DBP_NAME);
2809508192eSchristos dba_array_sort(entry, compare_names);
2819508192eSchristos dba_array_writelst(entry);
2829508192eSchristos }
2839508192eSchristos dba_array_FOREACH(pages, page) {
2849508192eSchristos dba_array_setpos(page, DBP_SECT, dba_tell());
2859508192eSchristos entry = dba_array_get(page, DBP_SECT);
2869508192eSchristos dba_array_sort(entry, compare_strings);
2879508192eSchristos dba_array_writelst(entry);
2889508192eSchristos }
2899508192eSchristos dba_array_FOREACH(pages, page) {
2909508192eSchristos if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) {
2919508192eSchristos dba_array_setpos(page, DBP_ARCH, dba_tell());
2929508192eSchristos dba_array_sort(entry, compare_strings);
2939508192eSchristos dba_array_writelst(entry);
2949508192eSchristos } else
2959508192eSchristos dba_array_setpos(page, DBP_ARCH, 0);
2969508192eSchristos }
2979508192eSchristos dba_array_FOREACH(pages, page) {
2989508192eSchristos dba_array_setpos(page, DBP_DESC, dba_tell());
2999508192eSchristos dba_str_write(dba_array_get(page, DBP_DESC));
3009508192eSchristos }
3019508192eSchristos dba_array_FOREACH(pages, page) {
3029508192eSchristos dba_array_setpos(page, DBP_FILE, dba_tell());
3039508192eSchristos dba_array_writelst(dba_array_get(page, DBP_FILE));
3049508192eSchristos }
3059508192eSchristos pos_end = dba_align();
3069508192eSchristos dba_seek(pos_pages);
3079508192eSchristos dba_array_FOREACH(pages, page)
3089508192eSchristos dba_array_writepos(page);
3099508192eSchristos dba_seek(pos_end);
3109508192eSchristos }
3119508192eSchristos
3129508192eSchristos static int
compare_names(const void * vp1,const void * vp2)3139508192eSchristos compare_names(const void *vp1, const void *vp2)
3149508192eSchristos {
3159508192eSchristos const char *cp1, *cp2;
3169508192eSchristos int diff;
3179508192eSchristos
3189508192eSchristos cp1 = *(const char * const *)vp1;
3199508192eSchristos cp2 = *(const char * const *)vp2;
3209508192eSchristos return (diff = *cp2 - *cp1) ? diff :
3219508192eSchristos strcasecmp(cp1 + 1, cp2 + 1);
3229508192eSchristos }
3239508192eSchristos
3249508192eSchristos static int
compare_strings(const void * vp1,const void * vp2)3259508192eSchristos compare_strings(const void *vp1, const void *vp2)
3269508192eSchristos {
3279508192eSchristos const char *cp1, *cp2;
3289508192eSchristos
3299508192eSchristos cp1 = *(const char * const *)vp1;
3309508192eSchristos cp2 = *(const char * const *)vp2;
3319508192eSchristos return strcmp(cp1, cp2);
3329508192eSchristos }
3339508192eSchristos
3349508192eSchristos /*** functions for handling macros ************************************/
3359508192eSchristos
3369508192eSchristos /*
3379508192eSchristos * In the hash table for a single macro, look up an entry by
3389508192eSchristos * the macro value or add an empty one if it doesn't exist yet.
3399508192eSchristos */
3409508192eSchristos static struct macro_entry *
get_macro_entry(struct ohash * macro,const char * value,int32_t np)3419508192eSchristos get_macro_entry(struct ohash *macro, const char *value, int32_t np)
3429508192eSchristos {
3439508192eSchristos struct macro_entry *entry;
3449508192eSchristos size_t len;
3459508192eSchristos unsigned int slot;
3469508192eSchristos
3479508192eSchristos slot = ohash_qlookup(macro, value);
3489508192eSchristos if ((entry = ohash_find(macro, slot)) == NULL) {
3499508192eSchristos len = strlen(value) + 1;
3509508192eSchristos entry = mandoc_malloc(sizeof(*entry) + len);
3519508192eSchristos memcpy(&entry->value, value, len);
3529508192eSchristos entry->pages = dba_array_new(np, DBA_GROW);
3539508192eSchristos ohash_insert(macro, slot, entry);
3549508192eSchristos }
3559508192eSchristos return entry;
3569508192eSchristos }
3579508192eSchristos
3589508192eSchristos /*
3599508192eSchristos * In addition to get_macro_entry(), add multiple page references,
3609508192eSchristos * converting them from the on-disk format (byte offsets in the file)
3619508192eSchristos * to page pointers in memory.
3629508192eSchristos */
3639508192eSchristos void
dba_macro_new(struct dba * dba,int32_t im,const char * value,const int32_t * pp)3649508192eSchristos dba_macro_new(struct dba *dba, int32_t im, const char *value,
3659508192eSchristos const int32_t *pp)
3669508192eSchristos {
3679508192eSchristos struct macro_entry *entry;
3689508192eSchristos const int32_t *ip;
3699508192eSchristos int32_t np;
3709508192eSchristos
3719508192eSchristos np = 0;
3729508192eSchristos for (ip = pp; *ip; ip++)
3739508192eSchristos np++;
3749508192eSchristos
3759508192eSchristos entry = get_macro_entry(dba_array_get(dba->macros, im), value, np);
3769508192eSchristos for (ip = pp; *ip; ip++)
3779508192eSchristos dba_array_add(entry->pages, dba_array_get(dba->pages,
3789508192eSchristos be32toh(*ip) / 5 / sizeof(*ip) - 1));
3799508192eSchristos }
3809508192eSchristos
3819508192eSchristos /*
3829508192eSchristos * In addition to get_macro_entry(), add one page reference,
3839508192eSchristos * directly taking the in-memory page pointer as an argument.
3849508192eSchristos */
3859508192eSchristos void
dba_macro_add(struct dba_array * macros,int32_t im,const char * value,struct dba_array * page)3869508192eSchristos dba_macro_add(struct dba_array *macros, int32_t im, const char *value,
3879508192eSchristos struct dba_array *page)
3889508192eSchristos {
3899508192eSchristos struct macro_entry *entry;
3909508192eSchristos
3919508192eSchristos if (*value == '\0')
3929508192eSchristos return;
3939508192eSchristos entry = get_macro_entry(dba_array_get(macros, im), value, 1);
3949508192eSchristos dba_array_add(entry->pages, page);
3959508192eSchristos }
3969508192eSchristos
3979508192eSchristos /*
3989508192eSchristos * Write the macros table to disk; the format is:
3999508192eSchristos * - The number of macro tables (actually, MACRO_MAX).
4009508192eSchristos * - That number of pointers to the individual macro tables.
4019508192eSchristos * - The individual macro tables.
4029508192eSchristos */
4039508192eSchristos static void
dba_macros_write(struct dba_array * macros)4049508192eSchristos dba_macros_write(struct dba_array *macros)
4059508192eSchristos {
4069508192eSchristos struct ohash *macro;
4079508192eSchristos int32_t im, pos_macros, pos_end;
4089508192eSchristos
4099508192eSchristos pos_macros = dba_array_writelen(macros, 1);
4109508192eSchristos im = 0;
4119508192eSchristos dba_array_FOREACH(macros, macro) {
4129508192eSchristos dba_array_setpos(macros, im++, dba_tell());
4139508192eSchristos dba_macro_write(macro);
4149508192eSchristos }
4159508192eSchristos pos_end = dba_tell();
4169508192eSchristos dba_seek(pos_macros);
4179508192eSchristos dba_array_writepos(macros);
4189508192eSchristos dba_seek(pos_end);
4199508192eSchristos }
4209508192eSchristos
4219508192eSchristos /*
4229508192eSchristos * Write one individual macro table to disk; the format is:
4239508192eSchristos * - The number of entries in the table.
4249508192eSchristos * - For each entry, two pointers, the first one to the value
4259508192eSchristos * and the second one to the list of pages.
4269508192eSchristos * - A list of values, each ending in a NUL byte.
4279508192eSchristos * - To assure alignment of following integers,
4289508192eSchristos * padding with NUL bytes up to a multiple of four bytes.
4299508192eSchristos * - A list of pointers to pages, each list ending in a 0 integer.
4309508192eSchristos */
4319508192eSchristos static void
dba_macro_write(struct ohash * macro)4329508192eSchristos dba_macro_write(struct ohash *macro)
4339508192eSchristos {
4349508192eSchristos struct macro_entry **entries, *entry;
4359508192eSchristos struct dba_array *page;
4369508192eSchristos int32_t *kpos, *dpos;
4379508192eSchristos unsigned int ie, ne, slot;
4389508192eSchristos int use;
4399508192eSchristos int32_t addr, pos_macro, pos_end;
4409508192eSchristos
4419508192eSchristos /* Temporary storage for filtering and sorting. */
4429508192eSchristos
4439508192eSchristos ne = ohash_entries(macro);
4449508192eSchristos entries = mandoc_reallocarray(NULL, ne, sizeof(*entries));
4459508192eSchristos kpos = mandoc_reallocarray(NULL, ne, sizeof(*kpos));
4469508192eSchristos dpos = mandoc_reallocarray(NULL, ne, sizeof(*dpos));
4479508192eSchristos
4489508192eSchristos /* Build a list of non-empty entries and sort it. */
4499508192eSchristos
4509508192eSchristos ne = 0;
4519508192eSchristos for (entry = ohash_first(macro, &slot); entry != NULL;
4529508192eSchristos entry = ohash_next(macro, &slot)) {
4539508192eSchristos use = 0;
4549508192eSchristos dba_array_FOREACH(entry->pages, page)
4559508192eSchristos if (dba_array_getpos(page))
4569508192eSchristos use = 1;
4579508192eSchristos if (use)
4589508192eSchristos entries[ne++] = entry;
4599508192eSchristos }
4609508192eSchristos qsort(entries, ne, sizeof(*entries), compare_entries);
4619508192eSchristos
4629508192eSchristos /* Number of entries, and space for the pointer pairs. */
4639508192eSchristos
4649508192eSchristos dba_int_write(ne);
4659508192eSchristos pos_macro = dba_skip(2, ne);
4669508192eSchristos
4679508192eSchristos /* String table. */
4689508192eSchristos
4699508192eSchristos for (ie = 0; ie < ne; ie++) {
4709508192eSchristos kpos[ie] = dba_tell();
4719508192eSchristos dba_str_write(entries[ie]->value);
4729508192eSchristos }
4739508192eSchristos dba_align();
4749508192eSchristos
4759508192eSchristos /* Pages table. */
4769508192eSchristos
4779508192eSchristos for (ie = 0; ie < ne; ie++) {
4789508192eSchristos dpos[ie] = dba_tell();
4799508192eSchristos dba_array_FOREACH(entries[ie]->pages, page)
4809508192eSchristos if ((addr = dba_array_getpos(page)))
4819508192eSchristos dba_int_write(addr);
4829508192eSchristos dba_int_write(0);
4839508192eSchristos }
4849508192eSchristos pos_end = dba_tell();
4859508192eSchristos
4869508192eSchristos /* Fill in the pointer pairs. */
4879508192eSchristos
4889508192eSchristos dba_seek(pos_macro);
4899508192eSchristos for (ie = 0; ie < ne; ie++) {
4909508192eSchristos dba_int_write(kpos[ie]);
4919508192eSchristos dba_int_write(dpos[ie]);
4929508192eSchristos }
4939508192eSchristos dba_seek(pos_end);
4949508192eSchristos
4959508192eSchristos free(entries);
4969508192eSchristos free(kpos);
4979508192eSchristos free(dpos);
4989508192eSchristos }
4999508192eSchristos
5009508192eSchristos static int
compare_entries(const void * vp1,const void * vp2)5019508192eSchristos compare_entries(const void *vp1, const void *vp2)
5029508192eSchristos {
5039508192eSchristos const struct macro_entry *ep1, *ep2;
5049508192eSchristos
5059508192eSchristos ep1 = *(const struct macro_entry * const *)vp1;
5069508192eSchristos ep2 = *(const struct macro_entry * const *)vp2;
5079508192eSchristos return strcmp(ep1->value, ep2->value);
5089508192eSchristos }
509