Annotation of mandoc/dba.c, Revision 1.6
1.6 ! schwarze 1: /* $Id: dba.c,v 1.5 2016/08/05 23:15:08 schwarze Exp $ */
1.1 schwarze 2: /*
3: * Copyright (c) 2016 Ingo Schwarze <schwarze@openbsd.org>
4: *
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.
8: *
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.
16: *
17: * Allocation-based version of the mandoc database, for read-write access.
18: * The interface is defined in "dba.h".
19: */
1.5 schwarze 20: #include "config.h"
21:
1.1 schwarze 22: #include <sys/types.h>
1.5 schwarze 23: #if HAVE_ENDIAN
1.4 schwarze 24: #include <endian.h>
1.5 schwarze 25: #elif HAVE_SYS_ENDIAN
26: #include <sys/endian.h>
27: #elif HAVE_NTOHL
28: #include <arpa/inet.h>
29: #endif
1.1 schwarze 30: #include <errno.h>
31: #include <stdint.h>
32: #include <stdlib.h>
33: #include <string.h>
34: #include <unistd.h>
35:
36: #include "mandoc_aux.h"
37: #include "mansearch.h"
38: #include "dba_write.h"
39: #include "dba_array.h"
40: #include "dba.h"
41:
42: static void *prepend(const char *, char);
43: static void dba_pages_write(struct dba_array *);
44: static int compare_names(const void *, const void *);
1.3 schwarze 45: static int compare_strings(const void *, const void *);
1.1 schwarze 46: static void dba_macros_write(struct dba_array *);
47: static void dba_macro_write(struct dba_array *);
48:
49:
50: /*** top-level functions **********************************************/
51:
52: struct dba *
53: dba_new(int32_t npages)
54: {
55: struct dba *dba;
56: int32_t im;
57:
58: dba = mandoc_malloc(sizeof(*dba));
59: dba->pages = dba_array_new(npages, DBA_GROW);
60: dba->macros = dba_array_new(MACRO_MAX, 0);
61: for (im = 0; im < MACRO_MAX; im++)
62: dba_array_set(dba->macros, im, dba_array_new(128, DBA_GROW));
63: return dba;
64: }
65:
66: void
67: dba_free(struct dba *dba)
68: {
69: struct dba_array *page, *macro, *entry;
70:
71: dba_array_FOREACH(dba->macros, macro) {
72: dba_array_undel(macro);
73: dba_array_FOREACH(macro, entry) {
74: free(dba_array_get(entry, 0));
75: dba_array_free(dba_array_get(entry, 1));
76: dba_array_free(entry);
77: }
78: dba_array_free(macro);
79: }
80: dba_array_free(dba->macros);
81:
82: dba_array_undel(dba->pages);
83: dba_array_FOREACH(dba->pages, page) {
84: dba_array_free(dba_array_get(page, DBP_NAME));
85: dba_array_free(dba_array_get(page, DBP_SECT));
86: dba_array_free(dba_array_get(page, DBP_ARCH));
87: free(dba_array_get(page, DBP_DESC));
88: dba_array_free(dba_array_get(page, DBP_FILE));
89: dba_array_free(page);
90: }
91: dba_array_free(dba->pages);
92:
93: free(dba);
94: }
95:
96: /*
97: * Write the complete mandoc database to disk; the format is:
98: * - One integer each for magic and version.
99: * - One pointer each to the macros table and to the final magic.
100: * - The pages table.
101: * - The macros table.
102: * - And at the very end, the magic integer again.
103: */
104: int
105: dba_write(const char *fname, struct dba *dba)
106: {
107: int save_errno;
108: int32_t pos_end, pos_macros, pos_macros_ptr;
109:
110: if (dba_open(fname) == -1)
111: return -1;
112: dba_int_write(MANDOCDB_MAGIC);
113: dba_int_write(MANDOCDB_VERSION);
114: pos_macros_ptr = dba_skip(1, 2);
115: dba_pages_write(dba->pages);
116: pos_macros = dba_tell();
117: dba_macros_write(dba->macros);
118: pos_end = dba_tell();
119: dba_int_write(MANDOCDB_MAGIC);
120: dba_seek(pos_macros_ptr);
121: dba_int_write(pos_macros);
122: dba_int_write(pos_end);
123: if (dba_close() == -1) {
124: save_errno = errno;
125: unlink(fname);
126: errno = save_errno;
127: return -1;
128: }
129: return 0;
130: }
131:
132:
133: /*** functions for handling pages *************************************/
134:
135: /*
136: * Create a new page and append it to the pages table.
137: */
138: struct dba_array *
139: dba_page_new(struct dba_array *pages, const char *name, const char *sect,
140: const char *arch, const char *desc, const char *file, enum form form)
141: {
142: struct dba_array *page, *entry;
143:
144: page = dba_array_new(DBP_MAX, 0);
145: entry = dba_array_new(1, DBA_STR | DBA_GROW);
146: dba_array_add(entry, prepend(name, NAME_FILE & NAME_MASK));
147: dba_array_add(page, entry);
148: entry = dba_array_new(1, DBA_STR | DBA_GROW);
149: dba_array_add(entry, (void *)sect);
150: dba_array_add(page, entry);
151: if (arch != NULL && *arch != '\0') {
152: entry = dba_array_new(1, DBA_STR | DBA_GROW);
153: dba_array_add(entry, (void *)arch);
154: } else
155: entry = NULL;
156: dba_array_add(page, entry);
157: dba_array_add(page, mandoc_strdup(desc));
158: entry = dba_array_new(1, DBA_STR | DBA_GROW);
159: dba_array_add(entry, prepend(file, form));
160: dba_array_add(page, entry);
161: dba_array_add(pages, page);
162: return page;
163: }
164:
165: /*
166: * Add a section, architecture, or file name to an existing page.
167: * Passing the NULL pointer for the architecture makes the page MI.
168: * In that case, any earlier or later architectures are ignored.
169: */
170: void
171: dba_page_add(struct dba_array *page, int32_t ie, const char *str)
172: {
173: struct dba_array *entries;
174: char *entry;
175:
176: entries = dba_array_get(page, ie);
177: if (ie == DBP_ARCH) {
178: if (entries == NULL)
179: return;
1.6 ! schwarze 180: if (str == NULL || *str == '\0') {
1.1 schwarze 181: dba_array_free(entries);
182: dba_array_set(page, DBP_ARCH, NULL);
183: return;
184: }
185: }
186: if (*str == '\0')
187: return;
1.2 schwarze 188: dba_array_FOREACH(entries, entry) {
189: if (ie == DBP_FILE && *entry < ' ')
190: entry++;
1.1 schwarze 191: if (strcmp(entry, str) == 0)
192: return;
1.2 schwarze 193: }
1.1 schwarze 194: dba_array_add(entries, (void *)str);
195: }
196:
197: /*
198: * Add an additional name to an existing page.
199: */
200: void
201: dba_page_alias(struct dba_array *page, const char *name, uint64_t mask)
202: {
203: struct dba_array *entries;
204: char *entry;
205: char maskbyte;
206:
207: if (*name == '\0')
208: return;
209: maskbyte = mask & NAME_MASK;
210: entries = dba_array_get(page, DBP_NAME);
211: dba_array_FOREACH(entries, entry) {
212: if (strcmp(entry + 1, name) == 0) {
213: *entry |= maskbyte;
214: return;
215: }
216: }
217: dba_array_add(entries, prepend(name, maskbyte));
218: }
219:
220: /*
221: * Return a pointer to a temporary copy of instr with inbyte prepended.
222: */
223: static void *
224: prepend(const char *instr, char inbyte)
225: {
226: static char *outstr = NULL;
227: static size_t outlen = 0;
228: size_t newlen;
229:
230: newlen = strlen(instr) + 1;
231: if (newlen > outlen) {
232: outstr = mandoc_realloc(outstr, newlen + 1);
233: outlen = newlen;
234: }
235: *outstr = inbyte;
236: memcpy(outstr + 1, instr, newlen);
237: return outstr;
238: }
239:
240: /*
241: * Write the pages table to disk; the format is:
242: * - One integer containing the number of pages.
243: * - For each page, five pointers to the names, sections,
244: * architectures, description, and file names of the page.
245: * MI pages write 0 instead of the architecture pointer.
246: * - One list each for names, sections, architectures, descriptions and
247: * file names. The description for each page ends with a NUL byte.
248: * For all the other lists, each string ends with a NUL byte,
249: * and the last string for a page ends with two NUL bytes.
250: * - To assure alignment of following integers,
251: * the end is padded with NUL bytes up to a multiple of four bytes.
252: */
253: static void
254: dba_pages_write(struct dba_array *pages)
255: {
1.3 schwarze 256: struct dba_array *page, *entry;
1.1 schwarze 257: int32_t pos_pages, pos_end;
258:
259: pos_pages = dba_array_writelen(pages, 5);
260: dba_array_FOREACH(pages, page) {
261: dba_array_setpos(page, DBP_NAME, dba_tell());
1.3 schwarze 262: entry = dba_array_get(page, DBP_NAME);
263: dba_array_sort(entry, compare_names);
264: dba_array_writelst(entry);
1.1 schwarze 265: }
266: dba_array_FOREACH(pages, page) {
267: dba_array_setpos(page, DBP_SECT, dba_tell());
1.3 schwarze 268: entry = dba_array_get(page, DBP_SECT);
269: dba_array_sort(entry, compare_strings);
270: dba_array_writelst(entry);
1.1 schwarze 271: }
272: dba_array_FOREACH(pages, page) {
273: if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) {
274: dba_array_setpos(page, DBP_ARCH, dba_tell());
1.3 schwarze 275: dba_array_sort(entry, compare_strings);
1.1 schwarze 276: dba_array_writelst(entry);
277: } else
278: dba_array_setpos(page, DBP_ARCH, 0);
279: }
280: dba_array_FOREACH(pages, page) {
281: dba_array_setpos(page, DBP_DESC, dba_tell());
282: dba_str_write(dba_array_get(page, DBP_DESC));
283: }
284: dba_array_FOREACH(pages, page) {
285: dba_array_setpos(page, DBP_FILE, dba_tell());
286: dba_array_writelst(dba_array_get(page, DBP_FILE));
287: }
288: pos_end = dba_align();
289: dba_seek(pos_pages);
290: dba_array_FOREACH(pages, page)
291: dba_array_writepos(page);
292: dba_seek(pos_end);
293: }
294:
295: static int
296: compare_names(const void *vp1, const void *vp2)
297: {
298: const char *cp1, *cp2;
299: int diff;
300:
301: cp1 = *(char **)vp1;
302: cp2 = *(char **)vp2;
303: return (diff = *cp2 - *cp1) ? diff :
304: strcasecmp(cp1 + 1, cp2 + 1);
305: }
306:
1.3 schwarze 307: static int
308: compare_strings(const void *vp1, const void *vp2)
309: {
310: const char *cp1, *cp2;
311:
312: cp1 = *(char **)vp1;
313: cp2 = *(char **)vp2;
314: return strcmp(cp1, cp2);
315: }
1.1 schwarze 316:
317: /*** functions for handling macros ************************************/
318:
319: /*
320: * Create a new macro entry and append it to one of the macro tables.
321: */
322: void
323: dba_macro_new(struct dba *dba, int32_t im, const char *value,
324: const int32_t *pp)
325: {
326: struct dba_array *entry, *pages;
327: const int32_t *ip;
328: int32_t np;
329:
330: np = 0;
331: for (ip = pp; *ip; ip++)
332: np++;
333: pages = dba_array_new(np, DBA_GROW);
334: for (ip = pp; *ip; ip++)
335: dba_array_add(pages, dba_array_get(dba->pages,
336: be32toh(*ip) / 5 / sizeof(*ip) - 1));
337:
338: entry = dba_array_new(2, 0);
339: dba_array_add(entry, mandoc_strdup(value));
340: dba_array_add(entry, pages);
341:
342: dba_array_add(dba_array_get(dba->macros, im), entry);
343: }
344:
345: /*
346: * Look up a macro entry by value and add a reference to a new page to it.
347: * If the value does not yet exist, create a new macro entry
348: * and add it to the macro table in question.
349: */
350: void
351: dba_macro_add(struct dba_array *macros, int32_t im, const char *value,
352: struct dba_array *page)
353: {
354: struct dba_array *macro, *entry, *pages;
355:
356: if (*value == '\0')
357: return;
358: macro = dba_array_get(macros, im);
359: dba_array_FOREACH(macro, entry)
360: if (strcmp(value, dba_array_get(entry, 0)) == 0)
361: break;
362: if (entry == NULL) {
363: entry = dba_array_new(2, 0);
364: dba_array_add(entry, mandoc_strdup(value));
365: pages = dba_array_new(1, DBA_GROW);
366: dba_array_add(entry, pages);
367: dba_array_add(macro, entry);
368: } else
369: pages = dba_array_get(entry, 1);
370: dba_array_add(pages, page);
371: }
372:
373: /*
374: * Write the macros table to disk; the format is:
375: * - The number of macro tables (actually, MACRO_MAX).
376: * - That number of pointers to the individual macro tables.
377: * - The individual macro tables.
378: */
379: static void
380: dba_macros_write(struct dba_array *macros)
381: {
382: struct dba_array *macro;
383: int32_t im, pos_macros, pos_end;
384:
385: pos_macros = dba_array_writelen(macros, 1);
386: im = 0;
387: dba_array_FOREACH(macros, macro) {
388: dba_array_setpos(macros, im++, dba_tell());
389: dba_macro_write(macro);
390: }
391: pos_end = dba_tell();
392: dba_seek(pos_macros);
393: dba_array_writepos(macros);
394: dba_seek(pos_end);
395: }
396:
397: /*
398: * Write one individual macro table to disk; the format is:
399: * - The number of entries in the table.
400: * - For each entry, two pointers, the first one to the value
401: * and the second one to the list of pages.
402: * - A list of values, each ending in a NUL byte.
403: * - To assure alignment of following integers,
404: * padding with NUL bytes up to a multiple of four bytes.
405: * - A list of pointers to pages, each list ending in a 0 integer.
406: */
407: static void
408: dba_macro_write(struct dba_array *macro)
409: {
410: struct dba_array *entry, *pages, *page;
411: int empty;
412: int32_t addr, pos_macro, pos_end;
413:
414: dba_array_FOREACH(macro, entry) {
415: pages = dba_array_get(entry, 1);
416: empty = 1;
417: dba_array_FOREACH(pages, page)
418: if (dba_array_getpos(page))
419: empty = 0;
420: if (empty)
421: dba_array_del(macro);
422: }
423: pos_macro = dba_array_writelen(macro, 2);
424: dba_array_FOREACH(macro, entry) {
425: dba_array_setpos(entry, 0, dba_tell());
426: dba_str_write(dba_array_get(entry, 0));
427: }
428: dba_align();
429: dba_array_FOREACH(macro, entry) {
430: dba_array_setpos(entry, 1, dba_tell());
431: pages = dba_array_get(entry, 1);
432: dba_array_FOREACH(pages, page)
433: if ((addr = dba_array_getpos(page)))
434: dba_int_write(addr);
435: dba_int_write(0);
436: }
437: pos_end = dba_tell();
438: dba_seek(pos_macro);
439: dba_array_FOREACH(macro, entry)
440: dba_array_writepos(entry);
441: dba_seek(pos_end);
442: }
CVSweb