1 /* $Id: dba.c,v 1.10 2017/02/17 14:43:54 schwarze Exp $ */
3 * Copyright (c) 2016, 2017 Ingo Schwarze <schwarze@openbsd.org>
5 * Permission to use, copy, modify, and distribute this software for any
6 * purpose with or without fee is hereby granted, provided that the above
7 * copyright notice and this permission notice appear in all copies.
9 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
10 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
11 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
12 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
13 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
14 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
15 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
17 * Allocation-based version of the mandoc database, for read-write access.
18 * The interface is defined in "dba.h".
22 #include <sys/types.h>
26 #include <sys/endian.h>
28 #include <arpa/inet.h>
37 #include "mandoc_aux.h"
38 #include "mandoc_ohash.h"
39 #include "mansearch.h"
40 #include "dba_write.h"
41 #include "dba_array.h"
45 struct dba_array
*pages
;
49 static void *prepend(const char *, char);
50 static void dba_pages_write(struct dba_array
*);
51 static int compare_names(const void *, const void *);
52 static int compare_strings(const void *, const void *);
54 static struct macro_entry
55 *get_macro_entry(struct ohash
*, const char *, int32_t);
56 static void dba_macros_write(struct dba_array
*);
57 static void dba_macro_write(struct ohash
*);
58 static int compare_entries(const void *, const void *);
61 /*** top-level functions **********************************************/
64 dba_new(int32_t npages
)
70 dba
= mandoc_malloc(sizeof(*dba
));
71 dba
->pages
= dba_array_new(npages
, DBA_GROW
);
72 dba
->macros
= dba_array_new(MACRO_MAX
, 0);
73 for (im
= 0; im
< MACRO_MAX
; im
++) {
74 macro
= mandoc_malloc(sizeof(*macro
));
75 mandoc_ohash_init(macro
, 4,
76 offsetof(struct macro_entry
, value
));
77 dba_array_set(dba
->macros
, im
, macro
);
83 dba_free(struct dba
*dba
)
85 struct dba_array
*page
;
87 struct macro_entry
*entry
;
90 dba_array_FOREACH(dba
->macros
, macro
) {
91 for (entry
= ohash_first(macro
, &slot
); entry
!= NULL
;
92 entry
= ohash_next(macro
, &slot
)) {
93 dba_array_free(entry
->pages
);
99 dba_array_free(dba
->macros
);
101 dba_array_undel(dba
->pages
);
102 dba_array_FOREACH(dba
->pages
, page
) {
103 dba_array_free(dba_array_get(page
, DBP_NAME
));
104 dba_array_free(dba_array_get(page
, DBP_SECT
));
105 dba_array_free(dba_array_get(page
, DBP_ARCH
));
106 free(dba_array_get(page
, DBP_DESC
));
107 dba_array_free(dba_array_get(page
, DBP_FILE
));
108 dba_array_free(page
);
110 dba_array_free(dba
->pages
);
116 * Write the complete mandoc database to disk; the format is:
117 * - One integer each for magic and version.
118 * - One pointer each to the macros table and to the final magic.
120 * - The macros table.
121 * - And at the very end, the magic integer again.
124 dba_write(const char *fname
, struct dba
*dba
)
127 int32_t pos_end
, pos_macros
, pos_macros_ptr
;
129 if (dba_open(fname
) == -1)
131 dba_int_write(MANDOCDB_MAGIC
);
132 dba_int_write(MANDOCDB_VERSION
);
133 pos_macros_ptr
= dba_skip(1, 2);
134 dba_pages_write(dba
->pages
);
135 pos_macros
= dba_tell();
136 dba_macros_write(dba
->macros
);
137 pos_end
= dba_tell();
138 dba_int_write(MANDOCDB_MAGIC
);
139 dba_seek(pos_macros_ptr
);
140 dba_int_write(pos_macros
);
141 dba_int_write(pos_end
);
142 if (dba_close() == -1) {
152 /*** functions for handling pages *************************************/
155 * Create a new page and append it to the pages table.
158 dba_page_new(struct dba_array
*pages
, const char *arch
,
159 const char *desc
, const char *file
, enum form form
)
161 struct dba_array
*page
, *entry
;
163 page
= dba_array_new(DBP_MAX
, 0);
164 entry
= dba_array_new(1, DBA_STR
| DBA_GROW
);
165 dba_array_add(page
, entry
);
166 entry
= dba_array_new(1, DBA_STR
| DBA_GROW
);
167 dba_array_add(page
, entry
);
168 if (arch
!= NULL
&& *arch
!= '\0') {
169 entry
= dba_array_new(1, DBA_STR
| DBA_GROW
);
170 dba_array_add(entry
, (void *)arch
);
173 dba_array_add(page
, entry
);
174 dba_array_add(page
, mandoc_strdup(desc
));
175 entry
= dba_array_new(1, DBA_STR
| DBA_GROW
);
176 dba_array_add(entry
, prepend(file
, form
));
177 dba_array_add(page
, entry
);
178 dba_array_add(pages
, page
);
183 * Add a section, architecture, or file name to an existing page.
184 * Passing the NULL pointer for the architecture makes the page MI.
185 * In that case, any earlier or later architectures are ignored.
188 dba_page_add(struct dba_array
*page
, int32_t ie
, const char *str
)
190 struct dba_array
*entries
;
193 entries
= dba_array_get(page
, ie
);
194 if (ie
== DBP_ARCH
) {
197 if (str
== NULL
|| *str
== '\0') {
198 dba_array_free(entries
);
199 dba_array_set(page
, DBP_ARCH
, NULL
);
205 dba_array_FOREACH(entries
, entry
) {
206 if (ie
== DBP_FILE
&& *entry
< ' ')
208 if (strcmp(entry
, str
) == 0)
211 dba_array_add(entries
, (void *)str
);
215 * Add an additional name to an existing page.
218 dba_page_alias(struct dba_array
*page
, const char *name
, uint64_t mask
)
220 struct dba_array
*entries
;
226 maskbyte
= mask
& NAME_MASK
;
227 entries
= dba_array_get(page
, DBP_NAME
);
228 dba_array_FOREACH(entries
, entry
) {
229 if (strcmp(entry
+ 1, name
) == 0) {
234 dba_array_add(entries
, prepend(name
, maskbyte
));
238 * Return a pointer to a temporary copy of instr with inbyte prepended.
241 prepend(const char *instr
, char inbyte
)
243 static char *outstr
= NULL
;
244 static size_t outlen
= 0;
247 newlen
= strlen(instr
) + 1;
248 if (newlen
> outlen
) {
249 outstr
= mandoc_realloc(outstr
, newlen
+ 1);
253 memcpy(outstr
+ 1, instr
, newlen
);
258 * Write the pages table to disk; the format is:
259 * - One integer containing the number of pages.
260 * - For each page, five pointers to the names, sections,
261 * architectures, description, and file names of the page.
262 * MI pages write 0 instead of the architecture pointer.
263 * - One list each for names, sections, architectures, descriptions and
264 * file names. The description for each page ends with a NUL byte.
265 * For all the other lists, each string ends with a NUL byte,
266 * and the last string for a page ends with two NUL bytes.
267 * - To assure alignment of following integers,
268 * the end is padded with NUL bytes up to a multiple of four bytes.
271 dba_pages_write(struct dba_array
*pages
)
273 struct dba_array
*page
, *entry
;
274 int32_t pos_pages
, pos_end
;
276 pos_pages
= dba_array_writelen(pages
, 5);
277 dba_array_FOREACH(pages
, page
) {
278 dba_array_setpos(page
, DBP_NAME
, dba_tell());
279 entry
= dba_array_get(page
, DBP_NAME
);
280 dba_array_sort(entry
, compare_names
);
281 dba_array_writelst(entry
);
283 dba_array_FOREACH(pages
, page
) {
284 dba_array_setpos(page
, DBP_SECT
, dba_tell());
285 entry
= dba_array_get(page
, DBP_SECT
);
286 dba_array_sort(entry
, compare_strings
);
287 dba_array_writelst(entry
);
289 dba_array_FOREACH(pages
, page
) {
290 if ((entry
= dba_array_get(page
, DBP_ARCH
)) != NULL
) {
291 dba_array_setpos(page
, DBP_ARCH
, dba_tell());
292 dba_array_sort(entry
, compare_strings
);
293 dba_array_writelst(entry
);
295 dba_array_setpos(page
, DBP_ARCH
, 0);
297 dba_array_FOREACH(pages
, page
) {
298 dba_array_setpos(page
, DBP_DESC
, dba_tell());
299 dba_str_write(dba_array_get(page
, DBP_DESC
));
301 dba_array_FOREACH(pages
, page
) {
302 dba_array_setpos(page
, DBP_FILE
, dba_tell());
303 dba_array_writelst(dba_array_get(page
, DBP_FILE
));
305 pos_end
= dba_align();
307 dba_array_FOREACH(pages
, page
)
308 dba_array_writepos(page
);
313 compare_names(const void *vp1
, const void *vp2
)
315 const char *cp1
, *cp2
;
318 cp1
= *(const char * const *)vp1
;
319 cp2
= *(const char * const *)vp2
;
320 return (diff
= *cp2
- *cp1
) ? diff
:
321 strcasecmp(cp1
+ 1, cp2
+ 1);
325 compare_strings(const void *vp1
, const void *vp2
)
327 const char *cp1
, *cp2
;
329 cp1
= *(const char * const *)vp1
;
330 cp2
= *(const char * const *)vp2
;
331 return strcmp(cp1
, cp2
);
334 /*** functions for handling macros ************************************/
337 * In the hash table for a single macro, look up an entry by
338 * the macro value or add an empty one if it doesn't exist yet.
340 static struct macro_entry
*
341 get_macro_entry(struct ohash
*macro
, const char *value
, int32_t np
)
343 struct macro_entry
*entry
;
347 slot
= ohash_qlookup(macro
, value
);
348 if ((entry
= ohash_find(macro
, slot
)) == NULL
) {
349 len
= strlen(value
) + 1;
350 entry
= mandoc_malloc(sizeof(*entry
) + len
);
351 memcpy(&entry
->value
, value
, len
);
352 entry
->pages
= dba_array_new(np
, DBA_GROW
);
353 ohash_insert(macro
, slot
, entry
);
359 * In addition to get_macro_entry(), add multiple page references,
360 * converting them from the on-disk format (byte offsets in the file)
361 * to page pointers in memory.
364 dba_macro_new(struct dba
*dba
, int32_t im
, const char *value
,
367 struct macro_entry
*entry
;
372 for (ip
= pp
; *ip
; ip
++)
375 entry
= get_macro_entry(dba_array_get(dba
->macros
, im
), value
, np
);
376 for (ip
= pp
; *ip
; ip
++)
377 dba_array_add(entry
->pages
, dba_array_get(dba
->pages
,
378 be32toh(*ip
) / 5 / sizeof(*ip
) - 1));
382 * In addition to get_macro_entry(), add one page reference,
383 * directly taking the in-memory page pointer as an argument.
386 dba_macro_add(struct dba_array
*macros
, int32_t im
, const char *value
,
387 struct dba_array
*page
)
389 struct macro_entry
*entry
;
393 entry
= get_macro_entry(dba_array_get(macros
, im
), value
, 1);
394 dba_array_add(entry
->pages
, page
);
398 * Write the macros table to disk; the format is:
399 * - The number of macro tables (actually, MACRO_MAX).
400 * - That number of pointers to the individual macro tables.
401 * - The individual macro tables.
404 dba_macros_write(struct dba_array
*macros
)
407 int32_t im
, pos_macros
, pos_end
;
409 pos_macros
= dba_array_writelen(macros
, 1);
411 dba_array_FOREACH(macros
, macro
) {
412 dba_array_setpos(macros
, im
++, dba_tell());
413 dba_macro_write(macro
);
415 pos_end
= dba_tell();
416 dba_seek(pos_macros
);
417 dba_array_writepos(macros
);
422 * Write one individual macro table to disk; the format is:
423 * - The number of entries in the table.
424 * - For each entry, two pointers, the first one to the value
425 * and the second one to the list of pages.
426 * - A list of values, each ending in a NUL byte.
427 * - To assure alignment of following integers,
428 * padding with NUL bytes up to a multiple of four bytes.
429 * - A list of pointers to pages, each list ending in a 0 integer.
432 dba_macro_write(struct ohash
*macro
)
434 struct macro_entry
**entries
, *entry
;
435 struct dba_array
*page
;
436 int32_t *kpos
, *dpos
;
437 unsigned int ie
, ne
, slot
;
439 int32_t addr
, pos_macro
, pos_end
;
441 /* Temporary storage for filtering and sorting. */
443 ne
= ohash_entries(macro
);
444 entries
= mandoc_reallocarray(NULL
, ne
, sizeof(*entries
));
445 kpos
= mandoc_reallocarray(NULL
, ne
, sizeof(*kpos
));
446 dpos
= mandoc_reallocarray(NULL
, ne
, sizeof(*dpos
));
448 /* Build a list of non-empty entries and sort it. */
451 for (entry
= ohash_first(macro
, &slot
); entry
!= NULL
;
452 entry
= ohash_next(macro
, &slot
)) {
454 dba_array_FOREACH(entry
->pages
, page
)
455 if (dba_array_getpos(page
))
458 entries
[ne
++] = entry
;
460 qsort(entries
, ne
, sizeof(*entries
), compare_entries
);
462 /* Number of entries, and space for the pointer pairs. */
465 pos_macro
= dba_skip(2, ne
);
469 for (ie
= 0; ie
< ne
; ie
++) {
470 kpos
[ie
] = dba_tell();
471 dba_str_write(entries
[ie
]->value
);
477 for (ie
= 0; ie
< ne
; ie
++) {
478 dpos
[ie
] = dba_tell();
479 dba_array_FOREACH(entries
[ie
]->pages
, page
)
480 if ((addr
= dba_array_getpos(page
)))
484 pos_end
= dba_tell();
486 /* Fill in the pointer pairs. */
489 for (ie
= 0; ie
< ne
; ie
++) {
490 dba_int_write(kpos
[ie
]);
491 dba_int_write(dpos
[ie
]);
501 compare_entries(const void *vp1
, const void *vp2
)
503 const struct macro_entry
*ep1
, *ep2
;
505 ep1
= *(const struct macro_entry
* const *)vp1
;
506 ep2
= *(const struct macro_entry
* const *)vp2
;
507 return strcmp(ep1
->value
, ep2
->value
);