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