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