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