Annotation of mandoc/dba.c, Revision 1.8
1.8 ! schwarze 1: /* $Id: dba.c,v 1.7 2016/08/17 18:59:37 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 *
1.8 ! schwarze 139: dba_page_new(struct dba_array *pages, const char *arch,
! 140: const char *desc, const char *file, enum form form)
1.1 schwarze 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(page, entry);
147: entry = dba_array_new(1, DBA_STR | DBA_GROW);
148: dba_array_add(page, entry);
149: if (arch != NULL && *arch != '\0') {
150: entry = dba_array_new(1, DBA_STR | DBA_GROW);
151: dba_array_add(entry, (void *)arch);
152: } else
153: entry = NULL;
154: dba_array_add(page, entry);
155: dba_array_add(page, mandoc_strdup(desc));
156: entry = dba_array_new(1, DBA_STR | DBA_GROW);
157: dba_array_add(entry, prepend(file, form));
158: dba_array_add(page, entry);
159: dba_array_add(pages, page);
160: return page;
161: }
162:
163: /*
164: * Add a section, architecture, or file name to an existing page.
165: * Passing the NULL pointer for the architecture makes the page MI.
166: * In that case, any earlier or later architectures are ignored.
167: */
168: void
169: dba_page_add(struct dba_array *page, int32_t ie, const char *str)
170: {
171: struct dba_array *entries;
172: char *entry;
173:
174: entries = dba_array_get(page, ie);
175: if (ie == DBP_ARCH) {
176: if (entries == NULL)
177: return;
1.6 schwarze 178: if (str == NULL || *str == '\0') {
1.1 schwarze 179: dba_array_free(entries);
180: dba_array_set(page, DBP_ARCH, NULL);
181: return;
182: }
183: }
184: if (*str == '\0')
185: return;
1.2 schwarze 186: dba_array_FOREACH(entries, entry) {
187: if (ie == DBP_FILE && *entry < ' ')
188: entry++;
1.1 schwarze 189: if (strcmp(entry, str) == 0)
190: return;
1.2 schwarze 191: }
1.1 schwarze 192: dba_array_add(entries, (void *)str);
193: }
194:
195: /*
196: * Add an additional name to an existing page.
197: */
198: void
199: dba_page_alias(struct dba_array *page, const char *name, uint64_t mask)
200: {
201: struct dba_array *entries;
202: char *entry;
203: char maskbyte;
204:
205: if (*name == '\0')
206: return;
207: maskbyte = mask & NAME_MASK;
208: entries = dba_array_get(page, DBP_NAME);
209: dba_array_FOREACH(entries, entry) {
210: if (strcmp(entry + 1, name) == 0) {
211: *entry |= maskbyte;
212: return;
213: }
214: }
215: dba_array_add(entries, prepend(name, maskbyte));
216: }
217:
218: /*
219: * Return a pointer to a temporary copy of instr with inbyte prepended.
220: */
221: static void *
222: prepend(const char *instr, char inbyte)
223: {
224: static char *outstr = NULL;
225: static size_t outlen = 0;
226: size_t newlen;
227:
228: newlen = strlen(instr) + 1;
229: if (newlen > outlen) {
230: outstr = mandoc_realloc(outstr, newlen + 1);
231: outlen = newlen;
232: }
233: *outstr = inbyte;
234: memcpy(outstr + 1, instr, newlen);
235: return outstr;
236: }
237:
238: /*
239: * Write the pages table to disk; the format is:
240: * - One integer containing the number of pages.
241: * - For each page, five pointers to the names, sections,
242: * architectures, description, and file names of the page.
243: * MI pages write 0 instead of the architecture pointer.
244: * - One list each for names, sections, architectures, descriptions and
245: * file names. The description for each page ends with a NUL byte.
246: * For all the other lists, each string ends with a NUL byte,
247: * and the last string for a page ends with two NUL bytes.
248: * - To assure alignment of following integers,
249: * the end is padded with NUL bytes up to a multiple of four bytes.
250: */
251: static void
252: dba_pages_write(struct dba_array *pages)
253: {
1.3 schwarze 254: struct dba_array *page, *entry;
1.1 schwarze 255: int32_t pos_pages, pos_end;
256:
257: pos_pages = dba_array_writelen(pages, 5);
258: dba_array_FOREACH(pages, page) {
259: dba_array_setpos(page, DBP_NAME, dba_tell());
1.3 schwarze 260: entry = dba_array_get(page, DBP_NAME);
261: dba_array_sort(entry, compare_names);
262: dba_array_writelst(entry);
1.1 schwarze 263: }
264: dba_array_FOREACH(pages, page) {
265: dba_array_setpos(page, DBP_SECT, dba_tell());
1.3 schwarze 266: entry = dba_array_get(page, DBP_SECT);
267: dba_array_sort(entry, compare_strings);
268: dba_array_writelst(entry);
1.1 schwarze 269: }
270: dba_array_FOREACH(pages, page) {
271: if ((entry = dba_array_get(page, DBP_ARCH)) != NULL) {
272: dba_array_setpos(page, DBP_ARCH, dba_tell());
1.3 schwarze 273: dba_array_sort(entry, compare_strings);
1.1 schwarze 274: dba_array_writelst(entry);
275: } else
276: dba_array_setpos(page, DBP_ARCH, 0);
277: }
278: dba_array_FOREACH(pages, page) {
279: dba_array_setpos(page, DBP_DESC, dba_tell());
280: dba_str_write(dba_array_get(page, DBP_DESC));
281: }
282: dba_array_FOREACH(pages, page) {
283: dba_array_setpos(page, DBP_FILE, dba_tell());
284: dba_array_writelst(dba_array_get(page, DBP_FILE));
285: }
286: pos_end = dba_align();
287: dba_seek(pos_pages);
288: dba_array_FOREACH(pages, page)
289: dba_array_writepos(page);
290: dba_seek(pos_end);
291: }
292:
293: static int
294: compare_names(const void *vp1, const void *vp2)
295: {
296: const char *cp1, *cp2;
297: int diff;
298:
299: cp1 = *(char **)vp1;
300: cp2 = *(char **)vp2;
301: return (diff = *cp2 - *cp1) ? diff :
302: strcasecmp(cp1 + 1, cp2 + 1);
303: }
304:
1.3 schwarze 305: static int
306: compare_strings(const void *vp1, const void *vp2)
307: {
308: const char *cp1, *cp2;
309:
310: cp1 = *(char **)vp1;
311: cp2 = *(char **)vp2;
312: return strcmp(cp1, cp2);
313: }
1.1 schwarze 314:
315: /*** functions for handling macros ************************************/
316:
317: /*
318: * Create a new macro entry and append it to one of the macro tables.
319: */
320: void
321: dba_macro_new(struct dba *dba, int32_t im, const char *value,
322: const int32_t *pp)
323: {
324: struct dba_array *entry, *pages;
325: const int32_t *ip;
326: int32_t np;
327:
328: np = 0;
329: for (ip = pp; *ip; ip++)
330: np++;
331: pages = dba_array_new(np, DBA_GROW);
332: for (ip = pp; *ip; ip++)
333: dba_array_add(pages, dba_array_get(dba->pages,
334: be32toh(*ip) / 5 / sizeof(*ip) - 1));
335:
336: entry = dba_array_new(2, 0);
337: dba_array_add(entry, mandoc_strdup(value));
338: dba_array_add(entry, pages);
339:
340: dba_array_add(dba_array_get(dba->macros, im), entry);
341: }
342:
343: /*
344: * Look up a macro entry by value and add a reference to a new page to it.
345: * If the value does not yet exist, create a new macro entry
346: * and add it to the macro table in question.
347: */
348: void
349: dba_macro_add(struct dba_array *macros, int32_t im, const char *value,
350: struct dba_array *page)
351: {
352: struct dba_array *macro, *entry, *pages;
353:
354: if (*value == '\0')
355: return;
356: macro = dba_array_get(macros, im);
357: dba_array_FOREACH(macro, entry)
358: if (strcmp(value, dba_array_get(entry, 0)) == 0)
359: break;
360: if (entry == NULL) {
361: entry = dba_array_new(2, 0);
362: dba_array_add(entry, mandoc_strdup(value));
363: pages = dba_array_new(1, DBA_GROW);
364: dba_array_add(entry, pages);
365: dba_array_add(macro, entry);
366: } else
367: pages = dba_array_get(entry, 1);
368: dba_array_add(pages, page);
369: }
370:
371: /*
372: * Write the macros table to disk; the format is:
373: * - The number of macro tables (actually, MACRO_MAX).
374: * - That number of pointers to the individual macro tables.
375: * - The individual macro tables.
376: */
377: static void
378: dba_macros_write(struct dba_array *macros)
379: {
380: struct dba_array *macro;
381: int32_t im, pos_macros, pos_end;
382:
383: pos_macros = dba_array_writelen(macros, 1);
384: im = 0;
385: dba_array_FOREACH(macros, macro) {
386: dba_array_setpos(macros, im++, dba_tell());
387: dba_macro_write(macro);
388: }
389: pos_end = dba_tell();
390: dba_seek(pos_macros);
391: dba_array_writepos(macros);
392: dba_seek(pos_end);
393: }
394:
395: /*
396: * Write one individual macro table to disk; the format is:
397: * - The number of entries in the table.
398: * - For each entry, two pointers, the first one to the value
399: * and the second one to the list of pages.
400: * - A list of values, each ending in a NUL byte.
401: * - To assure alignment of following integers,
402: * padding with NUL bytes up to a multiple of four bytes.
403: * - A list of pointers to pages, each list ending in a 0 integer.
404: */
405: static void
406: dba_macro_write(struct dba_array *macro)
407: {
408: struct dba_array *entry, *pages, *page;
409: int empty;
410: int32_t addr, pos_macro, pos_end;
411:
412: dba_array_FOREACH(macro, entry) {
413: pages = dba_array_get(entry, 1);
414: empty = 1;
415: dba_array_FOREACH(pages, page)
416: if (dba_array_getpos(page))
417: empty = 0;
418: if (empty)
419: dba_array_del(macro);
420: }
421: pos_macro = dba_array_writelen(macro, 2);
422: dba_array_FOREACH(macro, entry) {
423: dba_array_setpos(entry, 0, dba_tell());
424: dba_str_write(dba_array_get(entry, 0));
425: }
426: dba_align();
427: dba_array_FOREACH(macro, entry) {
428: dba_array_setpos(entry, 1, dba_tell());
429: pages = dba_array_get(entry, 1);
430: dba_array_FOREACH(pages, page)
431: if ((addr = dba_array_getpos(page)))
432: dba_int_write(addr);
433: dba_int_write(0);
434: }
435: pos_end = dba_tell();
436: dba_seek(pos_macro);
437: dba_array_FOREACH(macro, entry)
438: dba_array_writepos(entry);
439: dba_seek(pos_end);
440: }
CVSweb